/**
 * 编辑器文档
 */
function sDocument() {
    /**
     * 文本缩放比例
     */
    var scale = 1;

    /**
     * 文档缩放属性
     */
    const ZOOM = {
        /**
         * 获取缩放比
         */
        get scale(){
            return this.__scale ? this.__scale : 1;
        },
        /**
         * 设置缩放比
         */
        set scale(val){
            val = val > 3 ? 3 : val ? val : 1;
            if(val != this.__scale){
                triggerListener("zoom", contextListeners, val, 10);
            }
            this.__scale = val;
        }
    }

    /**
     * 设备像素比
     */
    var ratio = 1;

    /**
     * 缩放比例倍数
     */
    var ratioTime = 1;

    /**
     * 通用转换器
     */
    var converter = new sConverter();

    /**
     * json转换器
     */
    var jsonc = new sJSONConvertor();

    /**
     * xml转换器
     */
    var xmlc = new sXMLConvertor();

    /**
     * 工具类
     */
    var util = new sDocUtil();

    /**
     * 虚拟dom工厂
     */
    var domFactory = new sVdomFactory();

    /**
     * 组件集合
     */
    var components = {};

    /**
     * 包围盒集合
     */
    var boundRectList = [];

    /**
     * 当前行序号
     */
    var currentPageIndex = 0;

    /**
     * 外部容器ID
     */
    var containerId = null;

    /**
     * 外部容器
     */
    var container = null;

    /**
     * 编辑器外壳
     */
    var editor = null;

    /**
     * 内部容器
     */
    var icontainer = null;

    /**
     * 页面框架
     */
    var frame = null;

    /**
     * 画布大小
     */
    var baseSize = null;

    /**
     * 页面属性
     */
    var pageSetting = null;

    /**
     * 当前句柄
     */
    var handle = null;

    /**
     * 当前纸张规格
     */
    var paper = null;
    
    /**
     * 计数器
     */
    var counter = {
        /**
         * 输入域计数
         */
        inputArea: 0,
    };

    /**
     * 用户
     */
    var user = null;

    /**
     * 文档配置
     */
    var setting = new sSetting();

    /**
     * 页面垂直方向偏移量
     */
    const PAGE_OFFSET_TOP = 20;

    /**
     * 最大显示页面数
     */
    const MAX_PAGESIZE = 3;

    /**
     * 常量
     */
    const NodeType = {
        BASE:                    "scofi",
        EDITOR:                  "scofi-editor",
        ICONTAINER:              "scofi-icontainer",
        FRAME:                   "scofi-frame",
        FRAME_NAME:              "scofi-frame-id",
        PAGE:                    "scofi-page",
        PAGE_NAME:               "scofi-page-id",
        CANVAS_WRAPPER:          "scofi-canvas-wrapper",
        CANVAS_NAME:             "scofi-canvas-id",
        CARET:                   "scofi-caret",
        TEXT_INPUT:              "scofi-text-input",
        LAYER_RESIZE:            "scofi-layer-resize",
        LAYER_CONTENT:           "scofi-layer-content",
        LAYER_CORNER:            "scofi-layer-corner",
        DROP_DOWN:               "scofi-dropdown",
        DROP_DOWN_HEAD:          "scofi-dropdown-head",
        DROP_DOWN_BODY:          "scofi-dropdown-body",
        DROP_DOWN_FOOT:          "scofi-dropdown-foot",
        DROP_DOWN_SEARCH:        "scofi-dropdown-search",
        CELL_RESIZE:             "scofi-cell-resize",
        TABLE_OPERATOR:          "scofi-table-operator",
        TABLE_OPER_ROWS:         "scofi-oper-rows",
        TABLE_OPER_ROW:          "scofi-oper-row",
        TABLE_OPER_COLS:         "scofi-oper-cols",
        TABLE_OPER_COL:          "scofi-oper-col",
        TABLE_COL_ADD:           "scofi-col-add",
        TABLE_ROW_ADD:           "scofi-row-add",
        TABLE_COL_ADD_LINE:      "scofi-col-add-line",
        TABLE_ROW_ADD_LINE:      "scofi-row-add-line",
        TABLE_OPER_CORNER:       "scofi-oper-corner",
        TABLE_OPER_CONTAINER:    "scofi-oper-container",
        SPLITER:                 "scofi-spliter",
        SPLITER_NAME:            "scofi-spliter-id"
    }

    /**
     * 处理器
     */
    var processor = null;

    /**
     * 获取文档相关参数
     */
    this.getRenderParam = function(){
        return {
            scale: scale,
            ratio: ratio,
            ratioTime: ratioTime,
            zoomScale: ZOOM.scale
        }
    }

    /**
     * 呈现页最大数
     * @returns 
     */
    this.getMaxRenderSize = function(){
        return MAX_PAGESIZE < setting.multiplePageNum 
            ? setting.multiplePageNum 
            : MAX_PAGESIZE;
    }

    /**
     * 设置用户
     * @param {*} userInfo 
     */
    this.setUser = function(userInfo){
        user = userInfo;
    }

    /**
     * 设置处理器
     */
    this.setProcessor = function (sProcessor) {
        processor = sProcessor;
        processor.setDocument(this);

        //添加processor对document内部方法的引用
        processor.link("formatPageChange", formatPageChange.call(this));
        processor.link("removePage", removePage);
        processor.link("getPageRange", getPageRange);
        processor.link("computePageRange", computePageRange);

        processor.link("setHandle", setHandle);
        processor.link("getOffset", getOffset);

        processor.link("scrollTo", scrollTo);
        processor.link("getScrollTop", getScrollTop);

        processor.link("createMouseEvent", createMouseEventExt);

        processor.link("getSetting", getSetting);
        processor.link("getCounter", getCounter);
        processor.link("cloneData", cloneData);
        processor.link("getSelection", getSelection);

        processor.link("contentChange", contentChange);
        processor.link("contentStatistics", contentStatistics);
        processor.link("pageChange", pageChange);
    }

    /**
     * 获取处理器
     * @returns 
     */
    this.getProcessor = function() {
        return processor;
    }

    /**
     * 设置容器
     */
    this.setContainer = function (container) {
        containerId = container;
    }

    /**
     * 获取外部容器ID
     * @returns 
     */ 
    this.getContainerId = function(){
        return containerId;
    }

    /**
     * 初始化
     */
    this.build = function (opt) {
        if(opt && opt.paper){
            paper = opt.paper;
        }

        container = document.querySelector("#" + containerId);
        if (!container) return;
        
        container.innerHTML = "";
        clearPage();

        editor = createEditor.call(this, opt);
        icontainer = createInnerContainer.call(this, opt);
        frame = createFrame.call(this, opt);

        icontainer.appendChild(frame);
        editor.appendChild(icontainer);
        container.appendChild(editor);

        icontainer.style.minHeight = editor.clientHeight;

        clearPage.call(this);
        var pageIndex = this.insertPage(frame);

        setHandle(pageIndex);

        var data = parseData(getInitData());

        processor.build(data, {
            paperSize: baseSize,
            formatPageChange: formatPageChange.call(this, opt),
            load: (e)=>{
                triggerListener("load", contextListeners, {
                    setting: setting
                });
            }
        });

        registGlobalEvent.call(this);

        initVirtualDom();
    }

    /**
     * 初始化虚拟DOM
     * @param {*} option
     */
    var initVirtualDom = function(){
        var checkOpt = {};

        checkOpt.width = ssDict.sDefaultProps.CHECKBOX_WIDTH;
        checkOpt.height = ssDict.sDefaultProps.CHECKBOX_HEIGHT;
        checkOpt.fillColor = "#000";

        var checkbox_true = ssDict.sICON.prefix + svgToBase64ForVd(ssDict.sICON.checkbox.STATUS_TRUE, checkOpt);
        var checkbox_false = ssDict.sICON.prefix + svgToBase64ForVd(ssDict.sICON.checkbox.STATUS_FALSE, checkOpt);

        var radioOpt = {};

        radioOpt.width = ssDict.sDefaultProps.RADIO_WIDTH;
        radioOpt.height = ssDict.sDefaultProps.RADIO_HEIGHT;
        radioOpt.fillColor = "#000";

        var radio_true = ssDict.sICON.prefix + svgToBase64ForVd(ssDict.sICON.radio.STATUS_TRUE, radioOpt);
        var radio_false = ssDict.sICON.prefix + svgToBase64ForVd(ssDict.sICON.radio.STATUS_FALSE, radioOpt);
        
        var controls = {
            "sCheckBox": {
                control: sCheckBox,
                props: {
                    icons: {
                        checkedImg: checkbox_true,
                        uncheckImg: checkbox_false
                    },
                    size: {
                        width: checkOpt.width,
                        height: checkOpt.height
                    }
                }
            },
            "sRadio": {
                control: sRadio,
                props: {
                    icons: {
                        checkedImg: radio_true,
                        uncheckImg: radio_false
                    },
                    size: {
                        width: radioOpt.width,
                        height: radioOpt.height
                    }
                }
            },
        }

        for(var i in controls){
            let c = i;
            for(var j in controls[i].props){
                let prop = controls[i].props[j];
                if(j == "size"){
                    if(controls[i].control.prototype.hasOwnProperty(j)) continue;
                    Object.defineProperty(controls[i].control.prototype, j, {
                        get: function(){
                            return prop;
                        }
                    });
                }else if(j == "icons"){
                    for(var k in prop){
                        let name = k;
                        util.convertBase64ToImg(prop[name], 
                            (img)=>{
                                if(controls[c].control.prototype.hasOwnProperty(name)) return;
                                Object.defineProperty(controls[c].control.prototype, name, {
                                    get: function(){
                                        return img;
                                    }
                                });
                            }
                        );
                    }
                }
            }
        }
    }

    /**
     * 将svg图像设置相关属性并转成base64
     * @param {*} str svg xml字符串
     * @param {*} option 
     * @returns 
     */
    var svgToBase64ForVd = function(str, option){
        str = str.replace(/[\{]fillColor[\}]/, option.fillColor);
        str = str.replace(/[\{]width[\}]/, option.width);
        str = str.replace(/[\{]height[\}]/, option.height);
        str = util.convertStringToBase64(str);

        return str;
    }

    /**
     * 获取初始化数据
     * @returns 
     */
    var getInitData = function(){
        var areas = ["body", "head", "foot"];
        var children = [];

        for(var i = 0; i < areas.length; i++){
            var text = [];

            text.push({
                _sType: "sText",
                innerText: "",
            });
    
            text.push({
                _sType: "sParagraph",
                style: {
                    lineHeight: 1.5
                }
            });

            children.push(text);
        }


        return createBaseData(children[0], children[1], children[2]);
    }

    /**
     * 创建基础数据结构
     * @param {*} bodyChildren 
     * @param {*} headChildren 
     * @param {*} footChildren 
     * @returns 
     */
    var createBaseData = function(bodyChildren, headChildren, footChildren){
        var data = {
            _sType: "sRoot",
            children: [],
            ratioTime: ratioTime,
            ratio: ratio,
            style: {
            }
        };

        if(headChildren){
            data.children.push({
                _sType: "sPageHead",
                children: headChildren,
                style: {
                }
            });
        }

        if(bodyChildren){
            data.children.push({
                _sType: "sPageBody",
                children: bodyChildren,
                style: {
                }
            });
        }

        if(footChildren){
            data.children.push({
                _sType: "sPageFoot",
                children: footChildren,
                style: {
                }
            });
        }

        return data;
    }

    /**
     * 设置配置
     * @param {*} changeSetting {key:value} 键值对
     */
    this.setSetting = function(changeSetting){
        var oldSetting = cloneData(setting);

        setSetting(setting, changeSetting);

        if(needReformat(oldSetting)){
            var json = formatData(processor.export());
            this.load(json, {zoomScale: ZOOM.scale});
        }else{
            processor.invalidate({range: getPageRange()});
        }

        triggerListener("settingChange", contextListeners, changeSetting, 50);
    }

    /**
     * 更改配置
     * @param {*} setting
     * @param {*} changeSetting 
     */
    var setSetting = function(setting, changeSetting){
        if(changeSetting == null) return;

        for(var i in changeSetting){
            if(changeSetting[i] === undefined || setting[i] === undefined) continue;
            if(typeof(setting[i]) == "object"){
                setSetting(setting[i], changeSetting[i]);
            }else{
                setting[i] = changeSetting[i];
            }
        }
    }

    /**
     * 是否需要重新排版
     * @param {*} oldSetting 
     * @returns 
     */
    var needReformat = function(oldSetting){
        var flg = false;

        //留痕配置是否变化
        var leaveTrace = oldSetting.leaveTrace;
        var traceDisplay = oldSetting.traceDisplay;
         
        flg = flg || leaveTrace != setting.leaveTrace;
        flg = flg || (leaveTrace && 
            leaveTrace == setting.leaveTrace && 
            traceDisplay != setting.traceDisplay);

        //显示批注是否变化
        var annotationDisplay = oldSetting.annotation.display;
        var annotationWidth = oldSetting.annotation.width;

        flg = flg || annotationDisplay != setting.annotation.display;
        flg = flg || annotationWidth != setting.annotation.width;

        //多页显示是否变化
        flg = oldSetting.multiplePageNum != setting.multiplePageNum ||
            oldSetting.multipleSplitWidth != setting.multipleSplitWidth;
        
        return flg;
    }

    /**
     * 获取配置
     * @param {*} key
     * @returns 
     */
    this.getSetting = function(key){
        return getSetting(key);
    }

    /**
     * 获取配置
     * @param {*} key 如果指定key，则返回key的配置
     * @returns 
     */
    var getSetting = function(key){
        if(key){
            return setting[key];
        }

        return setting;
    }

    /**
     * 注册组件
     * @param {*} coms 组件集合
     */
    this.registComponent = function(coms){
        var com = null;
        for(var i in coms){
            com = coms[i];
            components[com.getName()] = com;
        }
    }

    /**
     * 加载json格式数据
     * @param {*} data json格式数据
     * @param {*} opt 可选参数
     */
    this.load = function (data, opt) {
        opt = opt ? opt : {};

        clearPage.call(this);

        var root = parseData(data);

        setPaperSize(root, opt);

        this.insertPage(frame);

        processor.build(root, { 
            paperSize: baseSize,
            isHeadEditable: opt.isHeadEditable,
            formatPageChange: formatPageChange.call(this, opt),
            load: (e)=>{
                let zScale = opt.zoomScale 
                    ? opt.zoomScale 
                    : ZOOM.scale;

                if(zScale != 1){
                    this.zoom(zScale);
                }

                triggerListener("load", contextListeners, {
                    setting: setting,
                    zoom: zScale
                });
            }
        });
    }

    /**
     * 设置纸张大小
     * @param {*} root 
     * @param {*} opt 
     */
    var setPaperSize = function(root, opt){
        if(opt.paper){
            paper = opt.paper;
        }else if(root.style){
            var dpi = getDPI();
            paper = {
                size: {
                    width: root.style.width 
                        ? root.style.width 
                        : Math.ceil(cmToPx(paperSpec.A4.size.width, dpi.x)),
                    height: root.style.height 
                        ? root.style.height 
                        : Math.ceil(cmToPx(paperSpec.A4.size.height, dpi.y))
                },
                padding: converter.parsePoM(root.style.padding)
            };

            setPadding(paper.padding, "left", "right", Math.ceil(cmToPx(paperSpec.A4.padding.left, dpi.x)));
            setPadding(paper.padding, "right", "left", Math.ceil(cmToPx(paperSpec.A4.padding.right, dpi.x)));
            setPadding(paper.padding, "top", "bottom", Math.ceil(cmToPx(paperSpec.A4.padding.top, dpi.y)));
            setPadding(paper.padding, "bottom", "top", Math.ceil(cmToPx(paperSpec.A4.padding.bottom, dpi.y)));

            if(root.style.headTop != null){
                paper.headTop = root.style.headTop;
            }

            if(root.style.footBottom != null){
                paper.footBottom = root.style.footBottom;
            }

            if(!validatePaper(paper)){
                paper = paperSpec["A4"];
            }
        }else{
            paper = paperSpec["A4"];
        }
    }

    /**
     * 设置内边距
     * @param {*} padding 内边距对象
     * @param {*} name 设置内边距属性名
     * @param {*} alterName 替补属性名
     * @param {*} defaultValue 默认值
     * @returns 
     */
    var setPadding = function(padding, name, alterName, defaultValue){
        if(padding[name] != null && padding[name] > 0) return;
        if(padding[alterName]){
            padding[name] = padding[alterName];
        }else{
            padding[name] = defaultValue;
        }
    }

    /**
     * 加载文件
     */
    this.loadFile = function(value, opt) {
        opt = opt ? opt : {};

        util.openFile("application/json,text/plain,text/xml", (e)=>{
            if(e.target.files.length == 0) return;

            if(opt.beforeLoad){
                opt.beforeLoad();
            }

            new Promise((resolve)=>{
                var file = e.target.files[0];
                var fr = new FileReader();
    
                fr.onload = (data)=>{
                    this.load(data.target.result);
                    resolve();
                }

                fr.readAsText(file);
            }).then(()=>{
                if(opt.afterLoad){
                    opt.afterLoad();
                }
            });
        });
    }

    /**
     * 导入文件
     */
    this.importFile = function(value, opt) {
        opt = opt ? opt : {};

        util.openFile("text/xml", (e)=>{
            if(e.target.files.length == 0) return;

            if(opt.beforeLoad){
                opt.beforeLoad();
            }

            new Promise((resolve)=>{
                var file = e.target.files[0];
                var fr = new FileReader();
    
                fr.onload = (data)=>{
                    var xml = xmlc.parseXML(data.target.result);
                    this.load(xml);
                    resolve();
                }

                fr.readAsText(file);
            }).then(()=>{
                if(opt.afterLoad){
                    opt.afterLoad();
                }
            });
        });
    }

    /**
     * 将所有内容用字符串的格式导出
     * @param {*} opt 
     */
    this.exportString = function(opt){
        var data = processor.export(opt);
        var json = formatData(data);

        return json;
    }

    /**
     * 导出数据文件
     */
    this.exportFile = function(opt) {
        data = formatData(processor.export(), opt && opt.useXML);

        util.exportfiles(data, "文档 " + 
            util.formatDate(new Date(), "yyyy-MM-dd") + 
            (opt && opt.useXML ? ".xml" : ".json"));
    }    

    /**
     * 格式化导出数据
     * @param {*} data 
     * @param {*} isXML
     * @returns 
     */
    var formatData = function(data, isXML){
        var baseData = createBaseData(data.body, data.head, data.foot);
        baseData.style = pageSettingToPageSize();

        delete data.head;
        delete data.body;
        delete data.foot;
        
        for(let i in data){
            baseData[i] = data[i];
        }

        baseData = util.compressJson(cloneData(baseData, (key, value) => {
            if(key.indexOf("__") == 0){
                return undefined;
            }else if(key == "_pType"){
                return undefined;
            }else if(!value){
                return undefined;
            }else if(key == "src"){
                return value.src
            }

            return value;
        }));

        if(isXML){
            return util.jsonToXml(baseData);
        }else{
            var json = JSON.stringify(baseData);
            return json;
        }
    }

    /**
     * 页面规格转页面样式大小
     * @returns 
     */
    var pageSettingToPageSize = function(){
        let pageSize = {};
        for(let i in pageSetting){
            if(i == "size"){
                for(let j in pageSetting[i]){
                    pageSize[j] = pageSetting[i][j];
                }
            }else{
                pageSize[i] = pageSetting[i];
            }
        }
        return pageSize;
    }

    /**
     * 计算当前页面大小和页面距
     * @returns 
     */
    var getPageSize = function(){
        var minusWidth = baseSize.annotationWidth 
            ? baseSize.annotationWidth * ZOOM.scale : 0;

        return {
            width: baseSize.width - minusWidth,
            height: baseSize.height,
            padding: baseSize.padding.top + "," + 
                (baseSize.padding.right - minusWidth * ratio) + "," + 
                baseSize.padding.bottom + "," + 
                baseSize.padding.left,
            headTop: baseSize.headTop,
            footBottom: baseSize.footBottom
        };
    }

    /**
     * 1导出为HTML
     */
    this.exportHTML = function(opt) {
        opt = opt ? opt : {};

        var pages = [];
        var pageNode = null;
        var lastWrap = null;

        //获取指定页HTML节点
        var getPageNode = (pageIndex)=>{
            let page = null;
            if(!pages[pageIndex]){
                page = document.createElement("div");
                page.classList.add("s-page");
                page.style.position = "relative";
                pages[pageIndex] = page;
            }else{
                page = pages[pageIndex];
            }
            return page;
        }

        //将canvas坐标转成html坐标
        var toPx = function(val){
            return (val / ratio).toFixed(2) + "px";
        }

        let zoomScale = null;

        if(ZOOM.scale != 1){
            zoomScale = ZOOM.scale;
        }

        this.zoom(1);

        var pageRange = parsePrintOption(opt);

        processor.customExport({
            range: pageRange,
            export: {
                exportPage: (page)=>{
                    pageNode = getPageNode(page.getIndex());
                    pageNode.style.width = toPx(page.getBoundRect().width);
                    pageNode.style.height = toPx(page.getBoundRect().height);
                },
                beforeExportText: (text, opt)=>{
                    lastWrap = document.createElement("div");

                    lastWrap.classList.add("s-text");
                    lastWrap.style.fontSize = opt.style.fontSize * scale / ratio;
                    lastWrap.style.fontFamily = opt.style.fontFamily;
                    lastWrap.style.fontWeight = opt.style.fontWeight;
                    lastWrap.style.fontStyle = opt.style.fontStyle;
                    lastWrap.style.color = opt.style.color;

                    pageNode = getPageNode(opt.pageIndex);
                    pageNode.appendChild(lastWrap);
                },
                exportText: (text, opt)=>{
                    let node = document.createElement("div");

                    node.style.position = "absolute";
                    node.style.zIndex = 0;
                    node.style.left = toPx(opt.left);
                    node.style.top = toPx(opt.top);

                    if(opt.containENAndCN && !/[\u4e00-\u9fa5]+/.test(text)){
                        //node.style.lineHeight = 1.5;
                    }
                    
                    node.innerText = text;

                    lastWrap.appendChild(node);
                },
                exportTextBackground: (boundRects, opt)=>{
                    let rect = null;
                    for(let i in boundRects){
                        rect = boundRects[i];
                        let node = document.createElement("div");

                        node.classList.add("s-text-background");
                        node.style.position = "absolute";
                        node.style.zIndex = 0;
                        node.style.left = toPx(rect.left);
                        node.style.top = toPx(rect.top);
                        node.style.width = toPx(rect.width);
                        node.style.height = toPx(rect.height);
                        node.style.backgroundColor = opt.style.backgroundColor;

                        pageNode = getPageNode(opt.pageIndex);
                        pageNode.appendChild(node);
                    }
                },
                exportTextDecoration: (lineStyle, opt)=>{
                    let node = document.createElement("div");

                    node.classList.add("s-text-decoration");
                    node.style.position = "absolute";
                    node.style.zIndex = 0;
                    node.style.left = toPx(opt.left);
                    node.style.top = toPx(opt.top);
                    node.style.width = toPx(opt.width);
                    node.style.height = toPx(opt.height);
                    node.style.backgroundColor = lineStyle.color;

                    pageNode = getPageNode(opt.pageIndex);
                    pageNode.appendChild(node);
                },
                exportLayer: function(layer, boundRect, opt){
                    if(layer.presentType == ssDict.sPresentType.IMAGE || 
                        (!layer.presentType || layer.getSubElement("sImage"))){
                        this._exportImage(layer, boundRect, opt);
                    }else if(layer.presentType == ssDict.sPresentType.LINE ||
                        layer.presentType == ssDict.sPresentType.PAGE_BREAK){
                        this._exportLine(layer, boundRect, opt);
                    }
                },
                _exportImage: (layer, boundRect, opt)=>{
                    let image = layer.getSubElement("sImage");
                    let node = document.createElement("img");

                    node.classList.add("s-layer-image");
                    node.src = image.src.currentSrc;
                    node.style.position = "absolute";
                    node.style.zIndex = 0;
                    node.style.left = toPx(boundRect.left);
                    node.style.top = toPx(boundRect.top);
                    node.style.width = toPx(boundRect.width);
                    node.style.height = toPx(boundRect.height);

                    pageNode = getPageNode(opt.pageIndex);
                    pageNode.appendChild(node);
                },
                _exportLine: (layer, boundRect, opt)=>{
                    let canvas = document.createElement("canvas");
                    
                    canvas.style.width = toPx(boundRect.width);
                    canvas.style.height = toPx(boundRect.height);
                    canvas.width = boundRect.width;
                    canvas.height = boundRect.height;

                    let ctx = canvas.getContext("2d");

                    ctx.putImageData(opt.imageData, 0, 0);
                    let image = canvas.toDataURL("image/png");

                    let node = document.createElement("img");

                    node.src = image;
                    node.classList.add("s-layer-line");
                    node.style.position = "absolute";
                    node.style.zIndex = 0;
                    node.style.left = toPx(boundRect.left);
                    node.style.top = toPx(boundRect.top);
                    node.style.width = toPx(boundRect.width);
                    node.style.height = toPx(boundRect.height);

                    pageNode = getPageNode(opt.pageIndex);
                    pageNode.appendChild(node); 
                },
                exportLayerBackground: (boundRects, opt)=>{
                    let rect = null;
                    for(let i in boundRects){
                        rect = boundRects[i];
                        let node = document.createElement("div");

                        node.classList.add("s-layer-background");
                        node.style.position = "absolute";
                        node.style.zIndex = 0;
                        node.style.left = toPx(rect.left);
                        node.style.top = toPx(rect.top);
                        node.style.width = toPx(rect.width);
                        node.style.height = toPx(rect.height);
                        node.style.backgroundColor = opt.style.backgroundColor;

                        pageNode = getPageNode(opt.pageIndex);
                        pageNode.appendChild(node);
                    }
                },
                exportLayerDecoration: (lineStyle, opt)=>{
                    let node = document.createElement("div");

                    node.classList.add("s-layer-decoration");
                    node.style.position = "absolute";
                    node.style.zIndex = 0;
                    node.style.left = toPx(opt.left);
                    node.style.top = toPx(opt.top);
                    node.style.width = toPx(opt.width);
                    node.style.height = toPx(opt.height);
                    node.style.backgroundColor = lineStyle ? lineStyle.color : "#000";

                    pageNode = getPageNode(opt.pageIndex);
                    pageNode.appendChild(node);
                },
                exportInputArea: (inputArea, opt)=>{
                    let node = document.createElement("div");

                    node.classList.add("s-input-" + 
                        (inputArea._sType == "sInputBegin" ? "begin" : "end")
                    );
                    node.style.position = "absolute";
                    node.style.zIndex = 0;
                    node.style.left = toPx(opt.left);
                    node.style.top = toPx(opt.top);
                    
                    node.style.fontSize = opt.style.fontSize * scale / ratio;
                    node.style.fontFamily = opt.style.fontFamily;
                    node.style.fontWeight = opt.style.fontWeight;
                    node.style.fontStyle = opt.style.fontStyle;
                    node.style.color = opt.style.color;

                    node.innerText = inputArea.tagView.innerText;

                    pageNode = getPageNode(opt.pageIndex);
                    pageNode.appendChild(node);
                },
                exportTable: (table, boundRect, opt)=>{
                    let pageIndex = opt.pageIndex;
                    pageNode = getPageNode(pageIndex);

                    let tableNode = document.createElement("table");
                    tableNode.classList.add("s-table");
                    tableNode.style.position = "absolute";
                    tableNode.style.zIndex = 0;
                    tableNode.style.borderCollapse = "collapse";
                    tableNode.style.left = toPx(boundRect.left - 3);
                    tableNode.style.top = toPx(boundRect.top);

                    pageNode.appendChild(tableNode);

                    let getRowspan = function(rows, cell, colIndex, pageIndex){
                        let rowspan = 0;
                        for(let i in rows){
                            let row = rows[i];
                            if(!row.__boundRect[pageIndex]) continue;
                            let c = row.getCells(colIndex);
                            if(cell === c){
                                rowspan++;
                            }else if(c.style.display == "none"){
                                if(cell === table.getSpanCell(i, colIndex)){
                                    rowspan++;
                                }
                            }
                        }
                        return rowspan;
                    }

                    let _setBorderStyle = function(td, name, borderStyle){
                        let borderWidth = borderStyle.width 
                            ? borderStyle.width 
                            : 1;
                        let lineDash = borderStyle.lineDash
                            ? borderStyle.lineDash
                            : "solid";
                        let color = borderStyle.color
                            ? borderStyle.color
                            : "#606266";

                        td.style[name] = borderWidth + "px " + lineDash + " " + color; 
                    }

                    let setBorderStyle = function(td, tName, name, style){
                        if(style[name] == "none") return;
                        if(!style[name]){
                            if(style.border && style.border != "none"){
                                _setBorderStyle(td, tName, style.border);
                            }
                        }else{
                             _setBorderStyle(td, tName, style[name]);                           
                        }
                    }

                    let borderType = {
                        "borderLeft": "border-left", 
                        "borderRight": "border-right", 
                        "borderTop": "border-top", 
                        "borderBottom": "border-bottom"
                    };
                    let spanCells = [];
                    let rows = table.getRows();
                    let exportRow = function(row, rowIndex, rowRect, pIndex, pageIndex){
                        let trNode = tableNode.insertRow();
                        trNode.style.height = toPx(rowRect.height);

                        let cells = row.getCells();
                        let rowspan = 1;
                        for(let j in cells){
                            let cell = cells[j];

                            rowspan = cell.rowspan;

                            if(cell.style.display == "none") {
                                let outParam = {};
                                
                                cell = table.getSpanCell(rowIndex, j, outParam);
                                if(spanCells.indexOf(cell) > -1) continue;

                                spanCells.push(cell);
                                rowspan = getRowspan(rows, cell, outParam.columnIndex, pageIndex);
                            }else if(cell.rowspan > 1 || cell.colspan > 1){
                                spanCells.push(cell);
                                rowspan = getRowspan(rows, cell, j, pageIndex);
                            }

                            let cellRect = cell.__boundRect[pIndex];
                            cellRect = cellRect[cellRect.length - 1];

                            let tdNode = trNode.insertCell();

                            tdNode.setAttribute("rowspan", rowspan);
                            tdNode.setAttribute("colspan", cell.colspan);
                            tdNode.style.width = toPx(cellRect.width);

                            for(let k in borderType){
                                setBorderStyle(tdNode, borderType[k], k, cell.style);
                            }
                        }
                    }

                    //表头存在
                    if(boundRect.headHeight){
                        let _pageIndex = 1;
                        for(let i in table.__boundRect){
                            _pageIndex = parseInt(i);
                            break;
                        }

                        for(let i in table.__headRows){
                            let row = table.getRows(table.__headRows[i]);

                            if(!row.__boundRect[_pageIndex]) continue;
    
                            let rowRect = row.__boundRect[_pageIndex];
                            rowRect = rowRect[rowRect.length - 1];
    
                            exportRow(row, i, rowRect, _pageIndex, _pageIndex);
                        }
                    }

                    let pIndex = opt.type == "body" ? pageIndex : 1;

                    for(let i in rows){
                        let row = rows[i];

                        if(!row.__boundRect[pIndex]) continue;

                        let rowRect = row.__boundRect[pIndex];
                        rowRect = rowRect[rowRect.length - 1];

                        exportRow(row, i, rowRect, pIndex, pageIndex);
                    }
                },
                exportWatermark: (content, rect, opt)=>{
                    let offsets = opt.translateOffsets;
                    for(let i in offsets){
                        let offset = offsets[i];
                        let node = document.createElement("div");

                        node.style.transform = "translate(" + toPx(offset.left) + "," + toPx(offset.top) + ")";
                        if(opt.rotate){
                            node.style.transform += " rotate(" + opt.rotate + "deg)";
                        }

                        node.style.position = "absolute";
                        node.style.zIndex = -1;
                        node.style.left = toPx(rect.left);
                        node.style.top = toPx(rect.top);

                        node.style.fontSize = opt.style.fontSize * scale / ratio;
                        node.style.fontFamily = opt.style.fontFamily;
                        node.style.fontWeight = opt.style.fontWeight;
                        node.style.fontStyle = opt.style.fontStyle;
                        node.style.color = opt.style.color;

                        node.innerText = content;

                        pageNode = getPageNode(opt.pageIndex);
                        pageNode.appendChild(node);
                    }
                },
                exportTexture: (texture, rect, opt)=>{
                    pageNode = getPageNode(opt.pageIndex);
                    if(texture.image){
                        let node = document.createElement("img");

                        node.src = texture.image.currentSrc;
                        node.style.position = "absolute";
                        node.style.zIndex = -2;
                        node.style.left = toPx(rect.left);
                        node.style.top = toPx(rect.top);
                        node.style.width = toPx(rect.width);
                        node.style.height = toPx(rect.height);
                        
                        pageNode.appendChild(node);
                    }else if(texture.style.backgroundColor){
                        pageNode.style.backgroundColor = texture.style.backgroundColor;
                    }
                }
            }
        });

        if(zoomScale){
            this.zoom(zoomScale);
        }

        var html = "<!DOCTYPE html>";

        html += "<head>";
        html += '<meta charset="utf-8">';

        if(!opt.noGlobalStyle){
            html += "<style>* {padding:0;margin:0;}</style>";
            html += "<style>@page {margin:0;}</style>";
            html += "<style>.s-page {margin-bottom: 2px;}</style>";
        }

        html += "</head>";
        html += "<body>";

        for(var i in pages){
            html += pages[i].outerHTML;
        }

        html += "</body>";
        html += "</html>";

        if(opt.notExportFile){
            return html;
        }else{
            util.exportfiles(html, "文档 " + 
            util.formatDate(new Date(), "yyyy-MM-dd") + ".html");
        }
    }

    /**
     * 导出PDF
     * @param {*} value 
     * @param {*} option
     */
    this.exportPDF = function(value, option){
        option = option ? option : option;

        new Promise((resolve)=>{
            if(option.beforeLoad){
                option.beforeLoad("正在转换，请稍候...");
                setTimeout(()=>{
                    resolve();
                }, 50);
            }
        }).then(()=>{
            return new Promise((resolve)=>{
                var html = this.exportHTML({notExportFile: true});
                resolve(html);
            });
        }).then((html)=>{
            let pageSetting = this.getPageSetting();
            let body = JSON.stringify({
                width: pageSetting.size.width + "cm",
                height: pageSetting.size.height + "cm",
                margin: {
                    top: pageSetting.padding.top + "cm",
                    right: pageSetting.padding.right + "cm",
                    bottom: pageSetting.padding.bottom + "cm",
                    left: pageSetting.padding.left + "cm",
                },
                html: html
            });

            fetch("http://localhost:3000/api/pdf", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json"
                },
                body: body
            }).then((rep)=>{
                new Response(rep.body).blob().then(blob => {
                    const data = new Blob([blob], { type: 'application/pdf' });
                    var save_link = document.createElementNS("http://www.w3.org/1999/xhtml", "a");
                    var urlObject = window.URL || window.webkitURL || window;
                    save_link.href = urlObject.createObjectURL(data);
                    save_link.download = "文档 " + util.formatDate(new Date(), "yyyy-MM-dd") + ".pdf";
                    save_link.click();
    
                    if(option.afterLoad){
                        option.afterLoad();
                    }
                });
            });
        })
    }

    /**
     * 解析打印可选项
     * @param {*} opt 
     * @returns 
     */
    var parsePrintOption = function(opt){
        var pageRange = null;
        //当前页
        if(opt.type == "current"){
            pageRange = [processor.getCurrentPageIndex()];
            pageRange.rowIndexOf = ()=>{ return 1; };
        }else if(opt.type == "continue"){
            if(opt.pageRange){
                pageRange = opt.pageRange;
                pageRange.rowIndexOf = ()=>{ return 1; };
            }else if(opt.startPageIndex){
                pageRange = {
                    startPageIndex: opt.startPageIndex,
                    startRowIndex: opt.startRowIndex,
                    endPageIndex: opt.endPageIndex,
                    endRowIndex: opt.endRowIndex,
                    indexOf: function(pageIndex){
                        return pageIndex >= this.startPageIndex && 
                            pageIndex <= this.endPageIndex ? 1 : -1;
                    },
                    rowIndexOf: function(pageIndex, rowIndex){
                        return ((
                            pageIndex == this.startPageIndex &&
                            rowIndex >= this.startRowIndex &&
                            pageIndex == this.endPageIndex &&
                            rowIndex <= this.endRowIndex
                        ) || (
                            pageIndex == this.startPageIndex && 
                            rowIndex >= this.startRowIndex &&
                            pageIndex < this.endPageIndex
                        ) || (
                            pageIndex > this.startPageIndex &&
                            pageIndex == this.endPageIndex &&
                            rowIndex <= this.endRowIndex
                        ) || (
                            pageIndex > this.startPageIndex &&
                            pageIndex < this.endPageIndex
                        )) ? 1 : -1;
                    }
                };
            }
        }else{
            pageRange = {
                indexOf: ()=>{ return 1; },
                rowIndexOf: ()=>{ return 1; }
            };
        }

        pageRange.watermark = opt.watermark != null ? opt.watermark : true;
        pageRange.texture = opt.texture != null ? opt.texture : true;
        pageRange.pageHead = opt.pageHead != null ? opt.pageHead : true;
        pageRange.pageFoot = opt.pageFoot != null ? opt.pageFoot : true;

        return pageRange;
    }

    /**
     * 打印文档
     * @param {*} opt 
     */
    this.print = function(opt){
        var iframe = document.createElement("iframe");
        iframe.style.visibility = "hidden";
        iframe.style.border = "none";
        iframe.style.left = 0;
        iframe.style.top = 0;
        iframe.style.width = 1000;
        iframe.style.height = 1000;

        document.body.append(iframe);

        var doc = iframe.contentWindow.document;
        var html = this.exportHTML({
            notExportFile: true,
            ...opt
        });
        doc.write(html);

        setTimeout(()=>{
            iframe.contentWindow.print();
            iframe.parentNode.removeChild(iframe);
        }, 200);
    }

    /**
     * 遍历并克隆对象
     * @param {*} obj 
     * @param {*} callback 
     */
    var cloneData = function(data, callback){
        var result = Array.isArray(data) ? [] : {};
        var item = null;

        for(var i in data){
            if(callback){
                item = callback(i, data[i]);
            }else{
                item = data[i];
            }

            if(typeof(data[i]) == "object" && (data[i] != null && Object.keys(data[i]).length > 0)){
                if(item !== undefined){
                    result[i] = cloneData(item, callback);
                }
            }else{
                if(item !== undefined){
                    result[i] = item;
                }
            }
        }

        return result;
    }

    /**
     * 获取文本内容
     */
    this.getText = function(){
        var texts = processor.getText();
        return texts.join("");
    }

    /**
     * 获取文档BODY文本内容
     */
    this.getBodyText = function(){
        var texts = processor.getText("body");
        return texts.join("");
    }

    /**
     * 排版页面改变事件
     * @param {*} opt 
     * @returns 
     */
    var formatPageChange = function(opt) {
        return function(pageSize){
            var rect = boundRectList[0];
            var height = 0;

            if(setting.multiplePageNum > 1){
                height = PAGE_OFFSET_TOP + rect.height * Math.ceil(pageSize / setting.multiplePageNum);
            }else{
                height = rect.top + (rect.height * (pageSize - rect.pageIndex + 1));
            }
    
            frame.style.height = height + "px";

            if(opt){
                if(opt.pageChange){
                    opt.pageChange(pageSize);
                }
            }
        }
    }

    /**
     * 获取当前元素
     */
    this.getCurrentElement = function(){
        return processor.getCurrentElement();
    }

    /**
     * 选择第一个符合选择器的元素
     * @param {*} selector 选择器
     */
    this.querySelector = function(selector){
        return processor.querySelector(selector);
    }

    /**
     * 选择所有符合选择器的元素
     * @param {*} selector 选择器
     */
    this.querySelectorAll = function(selector){
        return processor.querySelectorAll(selector);
    }

    /**
     * 创建元素
     */
    this.createElement = function(type, opt){
        return domFactory.createElement(type, opt);
    }

    /**
     * 获取画布
     * @param {*} handle 句柄
     */
    this.getCanvas = function(handle){
        return container.querySelector("[" + NodeType.CANVAS_NAME + "='" + handle + "']");
    }

    /**
     * 创建画布
     * @returns 
     */
    this.createCanvas = function(){
        return createCanvas({key: "canvas-print"});
    }

    /**
     * 全选
     */
    this.selectAll = function(){
    }

    /**
     * 针对空白文本元素设置的计数
     */
    var emptyTextCount = 0;
    
    /**
     * 解析数据
     * @param {*} data 数据对象
     * @returns 
     */
    var parseData = function(data) {
        if(!data) return null;

        var json = data;

        if (!(data instanceof Object)) {
            try{
                json = JSON.parse(data);
                json = util.decompressJson(json);
                if(!json._sType || json._sType != "sRoot"){
                    json = parseTxt(data);
                }
            }catch(e){
                json = parseTxt(data);
            }
        }else{
            json = util.decompressJson(json);
        }

        emptyTextCount = 0;

        var store = {
            begin:[]
        };
        _parseData([json], null, store);

        return json;
    }

    /**
     * 解析文本内容
     * @param {*} data 文本内容
     * @returns 
     */
    var parseTxt = function(data){
        data = jsonc.parseTxt(data);
        data = createBaseData(data.children);
        return data;
    }

    /**
     * 递归解析
     * @param {*} data 数据对象
     * @param {*} parentObj 父对象
     * @param {*} store 临时存储对象
     */
    var _parseData = function(data, parentObj, store) {
        for(var i in data){
            var element = data[i];
            if(!element._sType || element.initialize) continue;

            switch(element._sType){
                case "sRoot":
                    element = new sRoot(element);
                    break;
                case "sText":
                    element = new sText(element);
                    if(element.innerText == "" || element.innerText == null){
                        element.__fakeText = (emptyTextCount++).toString();
                    }
                    break;
                case "sParagraph":
                    element = new sParagraph(element);
                    break;    
                case "sBreakline":
                    element = new sBreakline(element);
                    break;  
                case "sTable":
                    element = new sTable(element);
                    _parseData(element.getColumns(), element, store);
                    _parseData(element.getRows(), element, store);
                    break;
                case "sTableColumn":
                    element = parentObj.instanceColumn(element);
                    break;
                case "sTableRow":
                    element = parentObj.instanceRow(element);
                    element.parent = parentObj;
                    Object.defineProperty(element, "parent", {enumerable: false});
                    _parseData(element.getCells(), element, store);
                    break;    
                case "sTableCell":
                    element = parentObj.instanceCell(element);
                    element.parent = parentObj;
                    Object.defineProperty(element, "parent", {enumerable: false});
                    complementElement(element);
                    break;
                case "sLayer":
                    element = new sLayer(element);
                    bindLayerEvent(element);
                    break;
                case "sImage":
                    element = new sImage(element);
                    if(typeof(element.src) == "string"){
                        let el = element;
                        util.convertBase64ToImg(el.src, (img)=>{
                            el.src = img;
                            el.dispatchEvent("load", {lazyTrigger: true});
                        });
                    }
                    break;
                case "sCheckBox":
                    element = new sCheckBox(element);
                    break;
                case "sRadio":
                    element = new sRadio(element);
                    break;
                case "sInputBegin":
                    counter.inputArea++;

                    element = sInputBegin(element);
                    
                    store.begin.push(element);
                    element.__name = "input_area_" + counter.inputArea;

                    processor.bindListener(element);
                    break;
                case "sInputEnd":
                    element = sInputEnd(element);

                    let begin = store.begin[store.begin.length - 1];
                    element.__name = begin.__name;
                    element.__beginTag = begin;
                    begin.__endTag = element;
                    
                    store.begin.splice(store.begin.length - 1, 1);
                    break;
                case "sPageHead":
                    element = new sPageHead(element);
                    complementElement(element);
                    break;
                case "sPageFoot":
                    element = new sPageFoot(element);
                    complementElement(element);
                    break;
                case "sPageBody":
                    element = new sPageBody(element);
                    complementElement(element);
                    break;
            }

            element.__nextSibling = null;
            element.__prevSibling = null;

            Object.defineProperty(element, "__nextSibling", {enumerable: false});
            Object.defineProperty(element, "__prevSibling", {enumerable: false});

            data[i] = element;

            if(element.children && element.children.length > 0){
                _parseData(element.children, parentObj, store);
            }

            //批注节点
            if(element.annotation){
                store.annotation = store.annotation ? store.annotation : {};

                if(store.annotation[element.annotation.id]){
                    element.annotation = store.annotation[element.annotation.id];
                }else{
                    store.annotation[element.annotation.id] = element.annotation;
                }

                if(element.annotationChildren){
                    _parseData(element.annotationChildren);
                }
            }
        }
    }

    /**
     * 获取计数器
     * @returns 
     */
    var getCounter = function(){
        return counter;
    }

    /**
     * 补充必须的元素
     * @param {*} element
     */
    var complementElement = function(element){
        if(element.children.length == 0){
            element.children.push(sDoc.createElement("sText"));
            element.children.push(sDoc.createElement("sParagraph"));
        }else if(element.children[element.children.length - 1]._sType != "sParagraph"){
            element.children.push(sDoc.createElement("sParagraph"));
        }

    }

    /**
     * 绑定层元素内置事件
     * @param {*} element
     */
    var bindLayerEvent = function(element){
        if(element.presentType){
            if(element.presentType == ssDict.sPresentType.RADIO){
                let name = null;
                for(var i in element.children){
                    if(element.children[i]._sType == "sRadio"){
                        name = element.children[i].name;
                        break;
                    }
                }

                if(!name) return;

                processor.getEvents().addEventListener("mouseup", {
                    name: name,
                    element: element
                })
            }
        }
    }

    /**
     * 插入新页
     * @param {*} frame 页面框架
     * @param {*} opt 可选项
     * 
     * @returns 返回新页的key
     */
    this.insertPage = function(frame, opt){
        if(!frame){
            frame = querySelector(
            "["+
                NodeType.FRAME_NAME + 
                "='" + 
                containerId + 
            "']");
        }

        var wrapper = null;
        var canvas = null;
        var page = null;
        var spliter = null;
        var pageIndex = -1;
        var index;

        opt = opt ? opt : {};

        if(opt.pageIndex){
            pageIndex = opt.pageIndex;
            index = pageIndex - 1;
        }else{
            pageIndex = createNewPageIndex.call(this);
        }

        wrapper = createCanvasWrapper.call(this);
        canvas = createCanvas.call(this, { key: pageIndex });
        spliter = createSpliter({ key: pageIndex })

        page = createPage({ key: pageIndex, canvas: canvas});

        processor.addHandle(pageIndex, index);

        wrapper.appendChild(canvas);
        page.appendChild(wrapper);
        page.appendChild(spliter);

        var ctx = canvas.getContext("2d");
        ctx.scale(ZOOM.scale, ZOOM.scale);

        processor.setScale(computeScale(ctx));

        let scaleOpt = {
            scale_font: parseFloat((getDPI().x / 72).toFixed(4)) * ratioTime,
            scale_cm: getDPI().x / 2.54 * getPixelRatio(ctx),
            scale_px: getPixelRatio(ctx), 
            scale_pt: scale
        };
        jsonc.setScale(scaleOpt);
        xmlc.setScale(scaleOpt);

        frame.appendChild(page);

        setFrameSizeAndPagePosition(frame, page, pageIndex);
        addPageBoundRect.call(this, page, pageIndex);

        return pageIndex;
    }

    /**
     * 设置frame大小和page的位置
     * @param {*} frame 
     * @param {*} page 
     * @param {*} pageIndex
     */
    var setFrameSizeAndPagePosition = function(frame, page, pageIndex){
        //计算页面位置和frame宽度
        let num = setting.multiplePageNum;
        let sw = setting.multipleSplitWidth;
        let pageSize = processor.getPageSize();

        if(num > 1 && pageSize > 1){
            let leftNum = (pageIndex - 1) % num;
            let topNum = Math.floor((pageIndex - 1) / num);

            page.style.left = page.offsetWidth * leftNum + sw * leftNum + "px";
            page.style.top = PAGE_OFFSET_TOP + (page.offsetHeight * topNum) + "px";

            frame.style.width = page.offsetWidth * num + sw * (num - 1) + "px";
            frame.style.height = PAGE_OFFSET_TOP + page.offsetHeight * Math.ceil(pageSize / num) + "px";
        }else{
            page.style.top = PAGE_OFFSET_TOP + (page.offsetHeight * (pageIndex - 1)) + "px";
            frame.style.width = page.offsetWidth + "px";
            frame.style.height = PAGE_OFFSET_TOP + page.offsetHeight * pageSize;
        }
    }

    /**
     * 获取指定页节点
     * @param {*} pageIndex 
     * @returns 
     */
    var getPage = function(pageIndex){
        return querySelector("." + NodeType.PAGE + "["+NodeType.PAGE_NAME+"='"+pageIndex+"']");
    }

    /**
     * 按页码移除页面
     * @param {*} pageIndex 页码
     */
    var removePage = function(pageIndex) {
        var page = getPage(pageIndex);
        if(!page) return;
        
        page.parentNode.removeChild(page);

        processor.removeHandle(pageIndex - 1);

        removePageBoundRect.call(this, pageIndex);
    }

    /**
     * 清除所有页面
     */
    var clearPage = function() {
        if(frame){
            frame.innerHTML = "";
        }
        
        caret.destroy();
        textInput.destroy();
        layerResize.destroy();
        cellResize.destroy();
        tableOperator.destroy();
        selection.getRangeAt().reset();

        boundRectList = [];
        currentPageIndex = 0;
        baseSize = null;
        handle = 1;
        ZOOM.scale = 1;
        counter = {
            inputArea: 0
        };

        processor.reset();
    }

    /**
     * 创建内部容器
     * @param {*} opt 
     * @returns 
     */
    var createInnerContainer = function(opt) {
        var icontainer = createElement("div");
        icontainer.classList.add(NodeType.ICONTAINER);

        return icontainer;
    }

    /**
     * 创建编辑外壳
     * @param {*} opt 
     * @returns 
     */
    var createEditor = function(opt) {
        var editor = createElement("div");

        editor.classList.add(NodeType.EDITOR);
        editor.addEventListener("scroll", (e)=>{
            reArrangePage.call(this, e.target, boundRectList[0]);
            triggerListener("scroll", contextListeners, e, 10);
        });

        if(opt && opt.size){
            if(opt.size.width != null){
                editor.style.width = parseFloat(opt.size.width) + "px";
            }
            if(opt.size.height != null){
                editor.style.height = parseFloat(opt.size.height) + "px";
            }
        }

        return editor;
    }

    /**
     * 重新分页
     * @param {*} t 
     * @param {*} rect 
     */
    var reArrangePage = function(t, rect){
        var pageSize = processor.getPageSize();
        if(pageSize == 0) return;

        var pageIndex = Math.ceil((t.scrollTop + t.clientHeight - PAGE_OFFSET_TOP) / rect.height);
        if(setting.multiplePageNum > 1){
            pageIndex = pageIndex * setting.multiplePageNum;
        }

        if(pageIndex > pageSize) return;
        
        reCreatePage(reComputeRange(pageSize, pageIndex));
    }

    /**
     * 重新计算需要展示的页面
     * @param {*} pageSize 
     * @param {*} pageIndex 
     */
    var reComputeRange = function(pageSize, pageIndex){
        var range = [];

        if(pageSize <= 2){
            for(var i = pageSize; i > 0; i--){
                range.push(i);
            }
            return range;
        }

        let size = MAX_PAGESIZE;
        if(setting.multiplePageNum > 1){
            size = size * setting.multiplePageNum;
        }

        var length = boundRectList.length > size ||
            (pageSize >= size && boundRectList.length < size)
            ? size 
            : boundRectList.length;

        var middle = null;
        if(length % 2 == 0){
            middle = length / 2 + 1;
        }else{
            middle = Math.ceil(length / 2);
        }

        if(pageSize - pageIndex < length - middle){
            for(var i = pageSize; i > pageSize - length; i--){
                range.push(i);
            }
        }else{
            var startIndex = pageIndex - middle;
            startIndex = startIndex <= 0 ? 1 : startIndex;
            for(var i = startIndex; i < startIndex + length; i++){
                range.push(i);
            }
        }

        return range;
    }

    /**
     * 计算当前需要展示的页码范围
     * @param {*} param0 
     * @returns 
     */
    var computePageRange = function({pageSize, pageIndex}){
        return reComputeRange(pageSize, pageIndex);
    }

    /**
     * 重新创建分页
     * @param {*} range 
     */
    var reCreatePage = function(range){
        var waitDelIndex = [];
        
        for(var i = 0; i < boundRectList.length; i++){
            var pageIndex = boundRectList[i].pageIndex;
            var index = range.indexOf(pageIndex);
            if(index < 0){
                waitDelIndex.push(pageIndex);
            }else{
                range.splice(index, 1);
            }
        }

        for(var i = 0; i < waitDelIndex.length; i++){
            removePage.call(this, waitDelIndex[i]);
        }

        if(range.length > 0){
            processor.invalidate({ range: range});
        }
    }

    /**
     * 创建框架节点
     * @param {*} opt 
     * @returns 
     */
    var createFrame = function (opt) {
        var frame = createElement("div");

        frame.classList.add(NodeType.FRAME);
        frame.setAttribute(NodeType.FRAME_NAME, containerId);
        frame.style.position = "relative";

        return frame;
    }

    /**
     * 创建canvas wrapper
     * @param {*} opt 
     * @returns 
     */
    var createCanvasWrapper = function (opt) {
        var wrapper = createElement("div");
        wrapper.classList.add(NodeType.CANVAS_WRAPPER);
        return wrapper;
    }

    /**
     * 是否点击在canvas范围
     */
    var mousedownInCanvas = false;

    /**
     * 创建画布
     */
    var createCanvas = function (opt) {
        var canvas = createElement("canvas");
        var ctx = canvas.getContext("2d", { willReadFrequently: true });

        var ratio = getPixelRatio(
            ctx, 
            util.isNumber(opt.ratioTime) 
                ? opt.ratioTime 
                : ratioTime
        );

        var size = getCanvasSize.call(this, ctx);

        canvas.style.width = size.width + "px";
        canvas.style.height = size.height + "px";

        canvas.width = size.width * ratio;
        canvas.height = size.height * ratio;

        canvas.style.background = "#fff";

        canvas.setAttribute(NodeType.CANVAS_NAME, opt.key);

        canvas.addEventListener("mousedown", (e)=>{
            mousedownInCanvas = true;
        })

        canvas.addEventListener("mouseup", (e)=>{
            mousedownInCanvas = false;
        })

        return canvas;
    }

    /**
     * 最后一个事件模拟对象
     */
    var lastMouseEvtExt = null;

    /**
     * 创建鼠标事件模拟对象
     */
    var createMouseEventExt = function(h){
        h = h != null ? h : handle;

        var canvas = getCanvas(null, null, h);
        var ctx = canvas.getContext("2d");
        var ratio = getPixelRatio(ctx);

        ratio /= ZOOM.scale;

        lastMouseEvtExt = {
            handle: h,
            caret: caret,
            textInput: textInput,
            layerResize: layerResize,
            cellResize: cellResize,
            tableOperator: tableOperator,
            selection: selection,
            baseOffset: getElementOffset(canvas, "relative"),
            ratio: ratio
        }

        return lastMouseEvtExt;
    }

    /**
     * 创建鼠标事件对象
     * @param {*} evt
     * @returns 
     */
    var createMouseEvent = function({evt, opt}){
        var canvas = getCanvas(evt, opt && opt.setHandle);
        if(!canvas) return null;

        if(opt && opt.setHandle){
            var ctx = canvas.getContext("2d");
            var ratio = getPixelRatio(ctx);

            ratio /= ZOOM.scale;

            return {
                nativeEvt: evt,
                handle: handle,
                caret: caret,
                textInput: textInput,
                layerResize: layerResize,
                cellResize: cellResize,
                tableOperator: tableOperator,
                dropdown: dropdown,
                selection: selection,
                outRect: {
                    ...getElementOffset(editor),
                    width: editor.clientWidth,
                    height: editor.clientHeight
                },
                offset: convertOffset(
                    canvas, 
                    evt.pageX, 
                    evt.pageY,
                    ratio
                ),
                baseOffset: getElementOffset(canvas, "relative"),
                scrollOffset: getElementScrollOffset(canvas),
                ratio: ratio
            };
        }

        return {
            nativeEvt: evt,
            handle: handle,
            selection: selection
        };
    }

    /**
     * 创建键盘事件对象
     * @param {*} evt
     * @returns 
     */
    var createKeyEvent = function(evt){
        var canvas = getCanvas(evt);

        if(!canvas) {
            canvas = querySelector("[" + 
                NodeType.CANVAS_NAME + "='" + boundRectList[0].pageIndex + "']");
        }

        var ctx = canvas.getContext("2d");
        var ratio = getPixelRatio(ctx);

        ratio /= ZOOM.scale;

        return {
            nativeEvt: evt,
            handle: handle,
            caret: caret,
            textInput: textInput,
            layerResize: layerResize,
            tableOperator: tableOperator,
            selection: selection,
            dropdown: dropdown,
            ratio: ratio
        };
    }

    /**
     * 目标是否在容器内
     * @param {*} target 
     * @returns 
     */
    var isInContainer = function(target){
        for(var node = target; node != null; node = node.parentNode){
            if(!node.getAttribute) continue;
            if(node.getAttribute(NodeType.FRAME_NAME) == containerId){
                return true;
            }
        }
        return false;
    }

    /**
     * 全局键盘按下事件
     * @param {*} evt 
     */
    var globalKeydown = function() {
        var sDoc = this;

        return function(evt){
            if(setting.docMode == ssDict.sDocMode.LOCKED) return;
            //if(!isInContainer(evt.target)) return;

            var sEvt = createKeyEvent(evt);
            if(!sEvt) return;

            if(evt.shiftKey){
                sEvt.shift = true;
            }

            if(evt.ctrlKey){
                sEvt.ctrl = true;
            }

            switch(evt.keyCode){
                //Backspace键盘
                case 8:
                    sEvt.keyName = "Backspace";
                    sEvt.delete = true;
                    sEvt.outRect = calcOutRect();
                    break;
                //Tab键
                case 9:     
                    sEvt.keyName = "Tab";
                    sEvt.data = "\t"; 
                    sEvt.input = true;
                    evt.preventDefault();
                    break;
                case 13:
                    sEvt.keyName = "Enter";
                    sEvt.enter = true;
                    sEvt.outRect = calcOutRect();
                    break;
                //Shift
                case 16:
                    sEvt.keyName = "Shift";
                    break;
                //ArrowLeft
                case 37:
                    sEvt.keyName = "ArrowLeft";
                    sEvt.direction = true;
                    sEvt.outRect = calcOutRect();
                    break;
                //ArrowUp
                case 38:
                    sEvt.keyName = "ArrowUp";
                    sEvt.direction = true;
                    sEvt.outRect = calcOutRect();
                    break;
                //ArrowRight
                case 39:
                    sEvt.keyName = "ArrowRight";
                    sEvt.direction = true;
                    sEvt.outRect = calcOutRect();
                    break;
                //ArrowDown
                case 40:
                    sEvt.keyName = "ArrowDown";
                    sEvt.direction = true;
                    sEvt.outRect = calcOutRect();
                    break;
                //Delete
                case 46:
                    sEvt.keyName = "Delete";
                    sEvt.delete = true;
                    sEvt.outRect = calcOutRect();
                    break;
                //A
                case 65:
                    if(evt.ctrlKey){
                        sEvt.keyName = "A";
                        sEvt.selectAll = true;
                        evt.preventDefault();
                    }
                    break;
                //C
                case 67:
                    if(evt.ctrlKey){
                        sEvt.keyName = "C";
                        sEvt.copy = true;
                        evt.preventDefault();
                    }
                    break;
                //V
                case 86:
                    if(evt.ctrlKey){
                        sEvt.keyName = "V";
                        sEvt.paste = true;
                    }
                    break;
                //X
                case 88:
                    if(evt.ctrlKey){
                        sEvt.keyName = "X";
                        sEvt.cut = true;
                        evt.preventDefault();
                    }
                    break;
                //Y
                case 89:
                    if(evt.ctrlKey){
                        sEvt.keyName = "Y";
                        sEvt.redo = true;
                        sEvt.outRect = calcOutRect();
                        evt.preventDefault();
                    }
                    break;
                //Z
                case 90:
                    if(evt.ctrlKey){
                        sEvt.keyName = "Z";
                        sEvt.undo = true;
                        sEvt.outRect = calcOutRect();
                        evt.preventDefault();
                    }
                    break;
            }

            if(sEvt.keyName){
                evt.stopPropagation();
            }

            sEvt.global = true;
            sEvt.eventType = "keydown";

            sDoc.getProcessor().dispatchEvent(sEvt);
        }
    }

    /**
     * 窗口键盘按下事件
     * @param {*} evt 
     */
    var windowKeydown = function() {
        var sDoc = this;

        return function(evt){
            if(setting.docMode == ssDict.sDocMode.LOCKED) return;

            var sEvt = createKeyEvent(evt);
            if(!sEvt) return;

            if(evt.ctrlKey){
                sEvt.ctrl = true;
            }

            if(sEvt.caret.isActive()){
                switch(evt.keyCode){
                    //Backspace键盘
                    case 8:
                        sEvt.delete = true;
                        sEvt.keyName = "Backspace";
                        sEvt.outRect = calcOutRect();
                        break;
                    //ArrowLeft
                    case 37:
                        sEvt.direction = true;
                        sEvt.keyName = "ArrowLeft";
                        sEvt.outRect = calcOutRect();
                        break;
                    //ArrowUp
                    case 38:
                        sEvt.direction = true;
                        sEvt.keyName = "ArrowUp";
                        sEvt.outRect = calcOutRect();
                        break;
                    //ArrowRight
                    case 39:
                        sEvt.direction = true;
                        sEvt.keyName = "ArrowRight";
                        sEvt.outRect = calcOutRect();
                        break;
                    //ArrowDown
                    case 40:
                        sEvt.direction = true;
                        sEvt.keyName = "ArrowDown";
                        sEvt.outRect = calcOutRect();
                        break;
                    //Delete
                    case 46:
                        sEvt.delete = true;
                        sEvt.keyName = "Delete";
                        sEvt.outRect = calcOutRect();
                        break;
                    //C
                    case 67:
                        if(evt.ctrlKey){
                            sEvt.copy = true;
                            evt.preventDefault();
                        }
                        break;
                    //V
                    case 86:
                        if(evt.ctrlKey){
                            sEvt.paste = true;
                        }
                        break;
                }
            }

            switch(evt.keyCode){
                case 89:
                    if(evt.ctrlKey){
                        sEvt.redo = true;
                        sEvt.outRect = calcOutRect();
                        evt.preventDefault();
                    }
                    break;
                //Z
                case 90:
                    if(evt.ctrlKey){
                        sEvt.undo = true;
                        sEvt.outRect = calcOutRect();
                        evt.preventDefault();
                    }
                    break;
            }

            sEvt.global = true;
            sEvt.eventType = "keydown";
            sEvt.dropdown.triggerKeydown(evt);

            sDoc.getProcessor().dispatchEvent(sEvt);
        }
    }

    /**
     * 窗口鼠标按下事件
     */
    var windowMousedown = function() {
        return function(e){
        }
    }

    /**
     * 鼠标滚轮事件
     */
    var windowMousewheel = function() {
        var sDoc = this;

        return function(evt){
            if(setting.docMode == ssDict.sDocMode.LOCKED) return;
            
            if(evt.ctrlKey){
                var delta = evt.wheelDelta || -evt.detail;

                if (delta > 0) {
                    sDoc.zoomIn();
                } else if (delta < 0) {
                    sDoc.zoomOut();
                }

                var sEvt = createMouseEvent({
                    evt: evt, 
                    opt: {
                        setHandle: true
                    }
                });

                sEvt.mousewheelUp = delta > 0;
                sEvt.mousewheelDown = delta < 0;
                sEvt.ctrl = true;

                evt.preventDefault();

                sEvt.global = true;
                sEvt.eventType = "mousewheel";
    
                sDoc.getProcessor().dispatchEvent(sEvt);
            }
        }
    }

    /**
     * 窗口大小改变事件
     */
    var windowResize = function() {
        var sDoc = this;
        return function(evt){
            triggerListener("resize", contextListeners, sDoc.getEditBoundRect());
        };
    }

    /**
     * 计算editor包围盒
     */
    var calcOutRect = function(){
        return {
            ...getElementOffset(editor),
            width: editor.clientWidth,
            height: editor.clientHeight
        };
    }

    /**
     * 全局键盘弹起事件
     * @param {*} evt 
     */
    var globalKeyup = function() {
        var sDoc = this;

        return function(evt){
            if(setting.docMode == ssDict.sDocMode.LOCKED) return;
            if(!isInContainer(evt.target)) return;

            var sEvt = createKeyEvent(evt);
            if(!sEvt) return;

            switch(evt.keyCode){
                //Shift
                case 16:
                    sEvt.keyName = "Shift";
                    break;
            }

            sEvt.eventType = "keyup";
            sDoc.getProcessor().dispatchEvent(sEvt);
        }
    }
    
    /**
     * 全局鼠标按下事件
     */
    var globalMousedown = function() {
        var sDoc = this;

        return function(evt){
            if(setting.docMode == ssDict.sDocMode.LOCKED) return;
            if(!isInContainer(evt.target)) return;

            if(isEditor(evt.target)){
                return;
            }

            var sEvt = createMouseEvent({
                evt: evt, 
                opt: {
                    setHandle: true
                }
            });

            if(sEvt){
                sEvt.eventType = "mousedown";
                sDoc.getProcessor().dispatchEvent(sEvt);

                triggerListener(sEvt.eventType, 
                    contextListeners, sEvt, null, toListenerEventParam);
            }
        }
    }

    /**
     * 全局双击事件
     */
    var globalDbclick = function() {
        var sDoc = this;

        return function(evt){
            if(setting.docMode == ssDict.sDocMode.LOCKED) return;
            if(!isInContainer(evt.target)) return;

            if(isEditor(evt.target)){
                return;
            }

            var sEvt = createMouseEvent({
                evt: evt, 
                opt: {
                    setHandle: true
                }
            });

            if(sEvt){
                sEvt.eventType = "dblclick";
                sDoc.getProcessor().dispatchEvent(sEvt);
                
                triggerListener(sEvt.eventType, contextListeners, {
                    isHeadEditable: sEvt.isHeadEditable
                });
            }
        }
    }

    /**
     * 是否是外壳层
     * @param {*} target 
     * @returns 
     */
    var isEditor = function(target){
        var clazz = target.getAttribute("class");
        return clazz && clazz.indexOf(NodeType.EDITOR) > -1;
    }

    /**
     * 全局鼠标移动事件
     */
    var globalMousemove = function() {
        var sDoc = this;

        return function(evt){
            if(setting.docMode == ssDict.sDocMode.LOCKED) return;
            if(!isInContainer(evt.target)) return;

            var sEvt = createMouseEvent({
                evt: evt,
                opt: {
                    setHandle: true
                }
                // opt: selection.getRangeAt().__start > 0 ||
                //     layerResize.__start != null
                //         ? { setHandle: true } 
                //         : null
            });

            if(sEvt){
                sEvt.eventType = "mousemove";
                sDoc.getProcessor().dispatchEvent(sEvt);
            }

        }
    }

    /**
     * 全局鼠标弹起事件
     */
    var globalMouseup = function() {
        var sDoc = this;

        return function(evt){
            if(setting.docMode == ssDict.sDocMode.LOCKED) return;
            //if(!isInContainer(evt.target)) return;

            if(handle){
                var sEvt = createMouseEvent({
                    evt: evt,
                    opt: layerResize.__start != null ||
                        layerResize.__startMove != null ||
                        cellResize.__start != null
                            ? { setHandle: true } 
                            : null
                });
                
                if(sEvt){
                    sEvt.eventType = "mouseup";
                    sDoc.getProcessor().dispatchEvent(sEvt);
                }
            }
        }
    }

    /**
     * 获取当前canvas
     * @param {*} evt 
     * @param {*} setHandle
     * @param {*} h
     * @returns 
     */
    var getCanvas = function(evt, setHandle, h){
        if(evt && evt.target && evt.target.tagName == "CANVAS"){
            if(setHandle){
                handle = evt.target.getAttribute(NodeType.CANVAS_NAME);
            }
            return evt.target;
        }else{
            h = h != null ? h : handle;
            return querySelector("[" + NodeType.CANVAS_NAME + "='" + h + "']");
        }
    }

    /**
     * 全局事件映射
     */
    var globalEventMap = [
        {"type": "keydown", "listener": globalKeydown.call(this)},
        {"type": "keyup", "listener": globalKeyup.call(this)},
        {"type": "mousedown", "listener": globalMousedown.call(this)},
        {"type": "mousemove", "listener": globalMousemove.call(this)},
        {"type": "mouseup", "listener": globalMouseup.call(this)},
        {"type": "dblclick", "listener": globalDbclick.call(this)},
    ]

    /**
     * 窗口事件映射
     */
    var windowEventMap = [
        {"type": "keydown", "listener": windowKeydown.call(this)},
        {"type": "mousedown", "listener": windowMousedown.call(this)},
        {"type": "mousewheel", "listener": windowMousewheel.call(this), "option": { passive: false }},
        {"type": "resize", "listener": windowResize.call(this)}
    ]

    /**
     * 右键菜单事件
     * @param {*} e 
     */
    var contextmenu = function(e){
        e.preventDefault();
    }

    /**
     * 注册全局事件
     */
    var registGlobalEvent = function(){
        for(var i = 0; i < globalEventMap.length; i++){
            var event = globalEventMap[i];
            editor.removeEventListener(event.type, event.listener);
            editor.addEventListener(event.type, event.listener);
        }

        for(var i = 0; i < windowEventMap.length; i++){
            var event = windowEventMap[i];
            window.removeEventListener(event.type, event.listener);
            window.addEventListener(event.type, event.listener, event.option);
        }

        document.removeEventListener("contextmenu", contextmenu);
        document.addEventListener("contextmenu", contextmenu);
    }

    /**
     * 光标对象
     */
    var caret = {
        _build: function(){
            this._caret = querySelector("." + NodeType.CARET);
            if(!this._caret){
                this._caret = createElement("div");
                this._caret.classList.add(NodeType.CARET);
                frame.appendChild(this._caret);
            }
        },
        moveTo: function(rect){
            if(!this.mutate(rect)) return;

            this._lastRect = rect;

            if(!this._caret){
                this._build();
            }

            this._caret.classList.remove("active");
            this._caret.classList.remove("blink");

            this._caret.style.left = rect.left + "px";
            this._caret.style.top = rect.top + "px";
            this._caret.style.height = rect.height + "px";

            this.focus();
        },
        mutate: function(rect){
            if(!this._lastRect) {
                this._mutate = true;
                return true;
            }

            this._mutate = rect.left != this._lastRect.left ||
                rect.top != this._lastRect.top ||
                rect.height != this._lastRect.height;

            this._mutate = this._mutate || !this._caret.classList.contains("active");

            return this._mutate;
        },
        focus: function(){
            if(this._timeout){
                clearTimeout(this._timeout);
                this._timeout = null;
            }

            this._caret.classList.add("active");

            this._timeout = setTimeout(()=>{
                if(!this._caret) return;
                this._caret.classList.add("blink");
            }, 500);
        },
        blur: function(force){
            if(!force){
                if(!this._caret || !this._mutate) return;
            }
            
            if(this._timeout){
                clearTimeout(this._timeout);
                this._timeout = null;
            }

            this._caret.classList.remove("active");
            this._caret.classList.remove("blink");
        },
        isActive: function(){
            return this._caret && this._caret.classList.contains("active");
        },
        destroy: function(){
            delete this._caret;
            delete this._lastRect;
        }
    }

    /**
     * 对文档上下文的引用
     */
    var sDoc = this;
    
    /**
     * 内容类型
     */
    var ContentType = {
        IMAGE: "image",
        HTML: "text/html",
        PLAIN: "text/plain",
        RTF: "text/rtf"
    };

    /**
     * 文字输入框对象
     */
    var textInput = {
        _build: function(){
            this._textInput = querySelector("." + NodeType.TEXT_INPUT);
            if(!this._textInput){
                var inputIntercept = function(e){
                    if(e.inputType == "historyUndo" || e.inputType == "historyRedo") {
                        let keyCode = e.inputType == "historyUndo" ? 90 : 89;
                        let evt = new KeyboardEvent("keydown", {
                            ctrlKey: true, 
                            keyCode: keyCode, 
                        });
                        
                        editor.dispatchEvent(evt);
                        return true;
                    }

                    return false;
                };
                
                this._textInput = createElement("input");
                this._textInput.classList.add(NodeType.TEXT_INPUT);

                this._textInput.addEventListener("blur", (e)=>{
                    if(mousedownInCanvas){
                        mousedownInCanvas = false;
                        return;
                    }
                    caret.blur(true);
                });

                this._textInput.addEventListener("focus", (e)=>{
                    if(!this._notFocusCaret){
                        caret.focus();
                    }
                });

                this._textInput.addEventListener("compositionstart", (e)=>{
                    e.target.__isCN = true;
                });

                this._textInput.addEventListener("compositionend", (e)=>{
                    e.target.__isCN = false;
                    sendText(e);
                });

                this._textInput.addEventListener("input", (e)=>{
                    if(setting.docMode == ssDict.sDocMode.LOCKED) return;
                    if(e.target.__isCN) return;
                    if(inputIntercept(e)) return;
                    
                    sendText(e);
                });

                this._textInput.addEventListener("paste", (e)=>{
                    e.preventDefault();

                    if(setting.docMode == ssDict.sDocMode.LOCKED) return;
                    if(!caret.isActive()) return;
                    
                    var clipData = e.clipboardData;
                    
                    if (!(e.clipboardData && e.clipboardData.items)) {
                        return;
                    }

                    parseClipDataAndWrite.call(this, e, clipData.items);
                });

                frame.appendChild(this._textInput);
            }
        },
        receiveHTML: function(e, item, callback){
            if(!item){
                callback(null);
                return;
            }

            item.getAsString((data)=>{
                if(!data) {
                    callback(data);
                    return;
                };

                var htmlJson = jsonc.parseHTML(data, "body");
                var localJson = jsonc.convertToLocal(htmlJson);
                var data = parseData(localJson);

                sendRichText(e, data);
                callback(data);
            });
        },
        receivePlain: function(e, item, callback){
            if(!item){
                callback(null);
                return;
            }

            item.getAsString((data)=>{
                if(!data) {
                    callback(data);
                    return;
                };

                var localJson = jsonc.parseTxt(data);
                var data = parseData(localJson);

                sendRichText(e, data);
                callback(data);
            });
        },
        receiveRTF: function(e, item, callback){
            if(!item){
                callback(null);
                return;
            }

            item.getAsString((data)=>{
                // RTFParser.default.parseRtfFromString(data, (err, doc) => {
                //     var html = RTF2HTML.default.convert(doc);
                //     var htmlJson = jsonc.parseHTML(html, "body");
                //     var localJson = jsonc.convertToLocal(htmlJson);
                //     var data = parseData(localJson);
    
                //     sendRichText(e, data);
                //     callback(data);
                // });
            });
        },
        receiveImage: function(e, item, callback){
            if(!item){
                callback(null);
                return;
            }

            var file = item.getAsFile();
            var reader = new FileReader();

            reader.readAsDataURL(file);
            reader.onload=function(event){
                var base64 = event.target.result;
                sDoc.insertImage(base64);
            }
        },
        copy: function(elements) {
            copy(elements);
        },
        moveTo: function(rect){
            if(!this._textInput){
                this._build();
            }

            this._textInput.style.left = rect.left + "px";
            this._textInput.style.top = rect.top + "px";
            this._textInput.style.height = "10px";

            this._textInput.classList.add("active");
            setTimeout(()=>{
                this.focus();
            }, 50);
        },
        blur: function(){
            if(this._textInput){
                this._textInput.blur();
            }
        },
        focus: function(focusCaret){
            if(this._textInput){
                this._notFocusCaret = focusCaret;
                this._textInput.focus();
                this._notFocusCaret = !focusCaret;
            }
        },
        inActive: function(){
            this._textInput && this._textInput.classList.remove("active");
        },
        isActive: function(){
            return this._textInput && this._textInput.classList.contains("active");
        },
        destroy: function(){
            this._textInput = null;
        }
    }

    /**
     * 解析剪贴板数据并写入到文档
     * @param {*} e
     * @param {*} clipDataItems 
     */
    var parseClipDataAndWrite = function(e, clipDataItems){
        var content = {};

        for(var i = 0; i < clipDataItems.length; i++) {
            var item = clipDataItems[i];
            if(item.type.indexOf("image") > -1){
                content[ContentType.IMAGE] = item;
            }else{
                content[item.type] = item;
            }
        }

        //按优先级来执行粘贴内容操作
        new Promise((resolve, reject)=>{
            this.receiveImage(e, content[ContentType.IMAGE], (data)=>{
                if(data) return;
                resolve();
            });
        }).then(()=>{
            return new Promise((resolve, reject)=>{
                this.receiveHTML(e, content[ContentType.HTML], (data)=>{
                    if(data) return;
                    resolve();
                });
            });
        }).then(()=>{
            return new Promise((resolve, reject)=>{
                // this.receiveRTF(e, content[ContentType.RTF], (data)=>{
                //     if(data) return;
                //     resolve();
                // });
                resolve();
            });
        }).then(()=>{
            this.receivePlain(e, content[ContentType.PLAIN], (data)=>{
            });
        });
    }

    /**
     * 复制数据
     */
    var copyData = null;

    /**
     * 将内容复制到剪贴板
     * @param {*} elements 
     */
    var copy = async function(elements){
        var txts = jsonc.convertToTxts(elements);
        var html = jsonc.convertToHTML(elements);

        copyData = {
            type: "html",
            value: html
        };

        try{
            const txtContent = new Blob(txts, {type: "text/plain"});
            const htmlContent = new Blob([html], {type: "text/html"});

            const item = new ClipboardItem({
                [txtContent.type]: txtContent,
                [htmlContent.type]: htmlContent
            });
            
            await navigator.clipboard.write([item]);
        }catch(e){
            var div = document.createElement("div");
            
            div.setAttribute("contenteditable", true);
            div.style.backgroundColor = "#fff";
            div.innerHTML = html;
            document.body.appendChild(div);

            selectDomElement(div);
            document.execCommand("copy");

            document.body.removeChild(div);
        }
    }

    /**
     * 选中可编辑dom元素节点的内容
     * @param {*} element 
     */
    var selectDomElement = function(element) {
        if (document.selection) {
            var range = document.body.createTextRange();
            range.moveToElementText(element);
            range.select();
        } else if (window.getSelection) {
            var range = document.createRange();
            range.selectNodeContents(element);
            window.getSelection().removeAllRanges();
            window.getSelection().addRange(range);
        }
    }

    /**
     * 发送普通文本
     * @param {*} e 
     * @param {*} text
     * @param {*} opt
     */
    var sendText = function(e, text, opt){
        if(!e) return;

        var sEvt = createKeyEvent(e);

        sEvt.input = true;
        sEvt.eventType = "keydown";
        
        if(text === undefined){
            sEvt.data = textInput._textInput.value;
            textInput._textInput.value = "";
        }else{
            sEvt.data = text;
        }

        if(opt){
            if(opt.id){
                sEvt.id = opt.id;
            }
        }

        sDoc.getProcessor().dispatchEvent(sEvt);
    }

    /**
     * 发送富文本
     * @param {*} e 
     * @param {*} data 
     */
    var sendRichText = function(e, data){
        if(!e) return;

        var sEvt = createKeyEvent(e);

        sEvt.paste = true;
        sEvt.eventType = "keydown";
        sEvt.data = data;
        sEvt.outRect = calcOutRect();

        textInput._textInput.value = "";

        sDoc.getProcessor().dispatchEvent(sEvt);
    }

    /**
     * 框选对象
     */
    var layerResize = {
        _directions: [
            "top-left",
            "top-center",
            "top-right",
            "middle-left",
            "middle-right",
            "bottom-left",
            "bottom-center",
            "bottom-right",
        ],
        _build: function(){
            this._layerResize = querySelector("." + NodeType.LAYER_RESIZE);
            if(!this._layerResize){
                this._layerResize = createElement("div");
                this._layerResize.classList.add(NodeType.LAYER_RESIZE);
                this._layerResize.setAttribute("ondragstart", "return false");

                var content = createElement("div");
                content.classList.add(NodeType.LAYER_CONTENT);

                for(var i = 0; i < this._directions.length; i++){
                    var corner = createElement("div");

                    corner.classList.add(NodeType.LAYER_CORNER);
                    corner.classList.add(this._directions[i]);
                    corner.setAttribute("direction", this._directions[i]);

                    content.appendChild(corner);
                }

                this._layerResize.appendChild(content);
                frame.appendChild(this._layerResize);
            }
        },
        moveTo: function(rect){
            if(!this._layerResize){
                this._build();
            }

            this._layerResize.style.left = rect.left + "px";
            this._layerResize.style.top = rect.top + "px";
            this._layerResize.style.width = rect.width + "px";
            this._layerResize.style.height = rect.height + "px";

            this._layerResize.classList.add("active");
        },
        resize: function(rect){
            this._layerResize.style.width = rect.width + "px";
            this._layerResize.style.height = rect.height + "px";
        },
        blur: function(){
            if(!this._layerResize) return;
            this._layerResize.classList.remove("active");
        },
        isActive: function(){
            return this._layerResize && 
                this._layerResize.getAttribute("class").indexOf("active") > -1;
        },
        isTrigger: function(name){
            return name && this._directions.indexOf(name) > -1;
        },
        reset: function(){
            for(var i in this){
                if(i.indexOf("__") > -1){
                    delete this[i];
                }
            }
        },
        destroy: function(){
            this._layerResize = null;
        }
    }

    /**
     * 单元格边框拖动显示对象
     */
    var cellResize = {
        _build: function(){
            this._cellResize = querySelector("." + NodeType.CELL_RESIZE);
            if(!this._cellResize){
                this._cellResize = createElement("div");
                this._cellResize.classList.add(NodeType.CELL_RESIZE);
                this._cellResize.setAttribute("ondragstart", "return false");
                icontainer.appendChild(this._cellResize);
            }
        },
        moveTo: function(rect, horizontal){
            if(!this._cellResize){
                this._build();
            }

            var baseOffset = getElementOffset(frame, null, NodeType.ICONTAINER);

            if(horizontal){
                this._cellResize.classList.add("horizontal");

                this._cellResize.style.left = rect.left + "px";
                this._cellResize.style.top = baseOffset.top + rect.top + "px";
            }else{
                this._cellResize.classList.remove("horizontal");

                this._cellResize.style.left = baseOffset.left + rect.left + "px";
                this._cellResize.style.top = rect.top + "px";
            }

            this._cellResize.style.width =  rect.width + "px";
            this._cellResize.style.height = rect.height + "px";

            this._cellResize.classList.add("active");
        },
        blur: function(){
            if(!this._cellResize) return;
            this.reset();
            this._cellResize.classList.remove("active");
        },
        isActive: function(){
            return this._cellResize && 
                this._cellResize.getAttribute("class").indexOf("active") > -1;
        },
        reset: function(){
            for(var i in this){
                if(i.indexOf("__") > -1){
                    delete this[i];
                }
            }
        },
        destroy: function(){
            this._cellResize = null;
        }
    }

    /**
     * 表格操作显示对象
     */
    var tableOperator = {
        _build: function(){
            this._tableOperator = querySelector("." + NodeType.TABLE_OPERATOR);
            if(!this._tableOperator){
                this._tableOperator = createElement("div");
                oper = this._tableOperator;
                oper.classList.add(NodeType.TABLE_OPERATOR);
                oper.setAttribute("ondragstart", "return false");

                icontainer.appendChild(oper);

                var rowAddSelector = "." + NodeType.TABLE_OPER_ROWS + " ." + NodeType.TABLE_ROW_ADD;
                var colAddSelector = "." + NodeType.TABLE_OPER_COLS + " ." + NodeType.TABLE_COL_ADD;
                var rowAddLineSelector = "." + NodeType.TABLE_OPER_ROWS + " ." + NodeType.TABLE_ROW_ADD_LINE;
                var colAddLineSelector = "." + NodeType.TABLE_OPER_COLS + " ." + NodeType.TABLE_COL_ADD_LINE;

                oper.addEventListener("click", (e)=>{
                    var sE = {};

                    if(e.target.classList.contains(NodeType.TABLE_OPER_COL)){
                        sE.col = true;
                    }else if(e.target.classList.contains(NodeType.TABLE_OPER_ROW)){
                        sE.row = true;
                    }else if(e.target.classList.contains(NodeType.TABLE_COL_ADD)){
                        sE.colAdd = true;
                    }else if(e.target.classList.contains(NodeType.TABLE_ROW_ADD)){
                        sE.rowAdd = true;
                    }else if(e.target.classList.contains(NodeType.TABLE_OPER_CORNER)){
                        sE.corner = true;
                    }

                    if(sE.col || sE.row){
                        sE.index = parseInt(e.target.getAttribute("data-index"));
                        var clazz = sE.col ? NodeType.TABLE_OPER_COL : NodeType.TABLE_OPER_ROW;
                        var active = oper.querySelector("." + clazz + ".active");
                        if(active){
                            active.classList.remove("active");
                        }
                        e.target.classList.add("active");
                    }else if(sE.colAdd || sE.rowAdd){
                        sE.index = parseInt(e.target.getAttribute("data-index"));
                    }

                    if(this._delegateForClick){
                        this._delegateForClick.call(processor, sE);
                    }
                });

                oper.addEventListener("mousemove", (e)=>{
                    var sE = {};

                    if(e.target.classList.contains(NodeType.TABLE_OPER_COL)){
                        sE.col = true;
                    }else if(e.target.classList.contains(NodeType.TABLE_OPER_ROW)){
                        sE.row = true;
                    }else if(e.target.classList.contains(NodeType.TABLE_COL_ADD)){
                        sE.colAdd = true;
                    }else if(e.target.classList.contains(NodeType.TABLE_ROW_ADD)){
                        sE.rowAdd = true;
                    }else if(e.target.classList.contains(NodeType.TABLE_OPER_CORNER)){
                        sE.corner = true;
                    }

                    if(sE.col || sE.row){
                        sE.index = parseInt(e.target.getAttribute("data-index"));
                        if(sE.index == 0 && (this.__colLen > 1 || this.__rowLen > 1 )) return;

                        var offset = getElementOffset(e.target, "relative");
                        
                        if(sE.col){
                            sE.colAdd = true;

                            offset.left -= editor.scrollLeft;

                            var ow = e.target.clientWidth / 2;
                            ow = ow > 10 ? 10 : ow;

                            if(e.pageX >= offset.left && 
                                e.pageX < offset.left + ow && sE.index > 0){
                                sE.firstHalf = true;
                            }else if(e.pageX >= offset.left + e.target.clientWidth - ow && 
                                e.pageX < offset.left + e.target.clientWidth){
                                sE.secondHalf = true;
                            }
                        }else{
                            sE.rowAdd = true;

                            offset.top -= editor.scrollTop;

                            var oh = e.target.clientHeight / 2;
                            oh = oh > 10 ? 10 : oh;

                            if(e.pageY >= offset.top && 
                                e.pageY < offset.top + oh && sE.index > 0){
                                sE.firstHalf = true;
                            }else if(e.pageY >= offset.top + e.target.clientHeight - oh && 
                                e.pageY < offset.top + e.target.clientHeight){
                                sE.secondHalf = true;
                            }
                        }

                        oper.querySelector(rowAddLineSelector).classList.remove("active");
                        oper.querySelector(colAddLineSelector).classList.remove("active");
                        oper.querySelector(rowAddSelector).classList.remove("active");
                        oper.querySelector(colAddSelector).classList.remove("active");

                        if(!sE.firstHalf && !sE.secondHalf) return;

                        var selector = sE.row ? rowAddSelector : colAddSelector;
                        var addNode = oper.querySelector(selector);
                        
                        if(addNode){
                            addNode.classList.add("active");

                            if(sE.col){
                                addNode.style.top = -4;
                                addNode.style.left = sE.firstHalf 
                                    ? converter.px2Num(e.target.style.left) - addNode.clientWidth / 2 
                                    : converter.px2Num(e.target.style.left) + e.target.clientWidth - addNode.clientWidth / 2;
                            }else{
                                addNode.style.left = -4;
                                addNode.style.top = sE.firstHalf 
                                    ? converter.px2Num(e.target.style.top) - addNode.clientHeight / 2 
                                    : converter.px2Num(e.target.style.top) + e.target.clientHeight - addNode.clientHeight / 2;
                            }

                            addNode.setAttribute("data-index", sE.firstHalf ? sE.index : sE.index + 1);

                            if(sE.colAdd || sE.rowAdd){
                                var selector = sE.rowAdd ? rowAddLineSelector : colAddLineSelector;
                                var addLineNode = oper.querySelector(selector);
                                if(addLineNode){
                                    addLineNode.classList.add("active");
                                    if(sE.colAdd){
                                        var borderWidth = window.getComputedStyle(addLineNode).borderLeftWidth;
                                        var addNode = oper.querySelector(colAddSelector);
        
                                        addLineNode.style.left = converter.px2Num(addNode.style.left) + 
                                            (converter.px2Num(addNode.clientWidth) - 
                                            converter.px2Num(borderWidth)) / 2 - 0.5;
                                        addLineNode.style.top = converter.px2Num(addNode.style.top) +
                                            converter.px2Num(addNode.clientWidth) + 0.5;
        
                                        addLineNode.style.height = this._rect.height + 0.5 + "px";
                                    }else{
                                        var borderWidth = window.getComputedStyle(addLineNode).borderTopWidth;
                                        var addNode = oper.querySelector(rowAddSelector);
        
                                        addLineNode.style.left = converter.px2Num(addNode.style.left) + 
                                            converter.px2Num(addNode.clientWidth);
                                        addLineNode.style.top = converter.px2Num(addNode.style.top) +
                                            (converter.px2Num(addNode.clientHeight) - 
                                            converter.px2Num(borderWidth)) / 2;
                                        
                                        addLineNode.style.width = this._rect.width + 2 + "px";
                                    }
                                }
                                
                                sE.addLineNode = addLineNode;
                            }
                        }
                    }else if(sE.corner){
                        oper.querySelector(rowAddSelector).classList.remove("active");
                        oper.querySelector(colAddSelector).classList.remove("active");
                    }

                    if(this._delegateForMousemove){
                        this._delegateForMousemove(sE);
                    }

                });

                oper.addEventListener("mouseenter", (e)=>{
                    this.__enter = true;
                });

                oper.addEventListener("mouseleave", (e)=>{
                    this.__enter = false;
                });
            }
        },
        moveTo: function(rect, cols, rows, curCell, delegates){
            if(!this._tableOperator){
                this._build();
            }

            if(delegates){
                this._delegateForClick = delegates.delegateForClick;
                this._delegateForMousemove = delegates.delegateForMousemove;
            }

            var baseOffset = getElementOffset(frame, null, NodeType.ICONTAINER);
            var cornerSize = {
                width: 12,
                height: 12
            };

            this._rect = rect;

            var len = 2;

            this._tableOperator.style.left =  baseOffset.left + rect.left - cornerSize.width - len + "px";
            this._tableOperator.style.top =  baseOffset.top + rect.top - cornerSize.height - len + "px";

            this._tableOperator.innerHTML = "";

            var colsNode = createElement("div");
            colsNode.classList.add(NodeType.TABLE_OPER_COLS);

            var colsContainerNode = createElement("div");
            colsContainerNode.classList.add(NodeType.TABLE_OPER_CONTAINER);

            this.__colLen = cols.length;
            this.__rowLen = rows.length;

            //列
            for(var i = 0; i < cols.length; i++){
                var colNode = createElement("div");
                colNode.classList.add(NodeType.TABLE_OPER_COL);
                colNode.style.left = cols[i].left + "px";
                colNode.style.top = "0px";
                colNode.style.width = cols[i].width + "px";
                colNode.style.height = cornerSize.height + "px";
                colNode.setAttribute("data-index", cols[i].index);

                if(curCell.tableColIndex == i){
                    colNode.classList.add("active");
                }

                if(i == cols.length - 1){
                    colNode.classList.add("last");
                }

                colsContainerNode.appendChild(colNode);
            }

            var addNode = createElement("div");
            addNode.classList.add(NodeType.TABLE_COL_ADD);
            colsContainerNode.appendChild(addNode);

            var addLineNode = createElement("div");
            addLineNode.classList.add(NodeType.TABLE_COL_ADD_LINE);
            colsContainerNode.appendChild(addLineNode);

            colsNode.appendChild(colsContainerNode);

            colsNode.style.width = rect.width + "px";
            colsNode.style.height = cornerSize.height + "px";
            colsNode.style.left = cornerSize.width + "px";
            colsNode.style.top = "0px";

            var rowsNode = createElement("div");
            rowsNode.classList.add(NodeType.TABLE_OPER_ROWS);

            var rowsContainerNode = createElement("div");
            rowsContainerNode.classList.add(NodeType.TABLE_OPER_CONTAINER);

            var height = 0;

            //行
            for(var i = 0; i < rows.length; i++){
                var rowNode = createElement("div");
                rowNode.classList.add(NodeType.TABLE_OPER_ROW);
                rowNode.style.left = "0px";
                rowNode.style.top = rows[i].top + "px";
                rowNode.style.width = cornerSize.width + "px";
                rowNode.style.height = rows[i].height + "px";
                rowNode.setAttribute("data-index", rows[i].index);

                if(curCell.tableRowIndex == i){
                    rowNode.classList.add("active");
                }

                if(i == rows.length - 1){
                    rowNode.classList.add("last");
                }

                rowsContainerNode.appendChild(rowNode);
                height += rows[i].height;
            }

            addNode = createElement("div");
            addNode.classList.add(NodeType.TABLE_ROW_ADD);
            rowsContainerNode.appendChild(addNode);

            addLineNode = createElement("div");
            addLineNode.classList.add(NodeType.TABLE_ROW_ADD_LINE);
            rowsContainerNode.appendChild(addLineNode);

            rowsNode.appendChild(rowsContainerNode);

            rowsNode.style.width = cornerSize.width + "px";
            rowsNode.style.height = rect.height + "px";
            rowsNode.style.left = "0px";
            rowsNode.style.top = cornerSize.height + "px";

            var cornerNode = createElement("div");
            cornerNode.classList.add(NodeType.TABLE_OPER_CORNER);
            cornerNode.style.width = cornerSize.width + "px";
            cornerNode.style.height = cornerSize.height + "px";
            cornerNode.style.left = "0px";
            cornerNode.style.top = "0px";

            var containerNode = createElement("div");
            containerNode.classList.add(NodeType.TABLE_OPER_CONTAINER);

            containerNode.appendChild(colsNode);
            containerNode.appendChild(rowsNode);
            containerNode.appendChild(cornerNode);

            this._tableOperator.appendChild(containerNode);
            this._tableOperator.classList.add("active");
        },
        blur: function(){
            if(this.__timeout){
                clearTimeout(this.__timeout);
                this.__timeout = null;
            }

            this.__timeout = setTimeout(()=>{
                if(!this._tableOperator || this.__enter) return;
                this.reset();
                this._tableOperator.classList.remove("active");
            }, 500);
        },
        isActive: function(){
            return this._tableOperator && 
                this._tableOperator.getAttribute("class").indexOf("active") > -1;
        },
        reset: function(){
            for(var i in this){
                if(i.indexOf("__") > -1){
                    delete this[i];
                }
            }
        },
        destroy: function(){
            this.blur();
            this._tableOperator = null;
        }
    }

    /**
     * 下拉框
     */
    var dropdown = {
        _build: function(option){
            this.__option = this.__option ? this.__option : {};
            option = option ? option : {};

            if(!option.key || (this.__option.key && this.__option.key == option.key)) return;

            if(!this._dropdown){
                this._dropdown = createElement("div");
                this._dropdown.classList.add(NodeType.DROP_DOWN);
                this._dropdown.setAttribute("ondragstart", "return false");
            }

            let isList = !option.type || option.type == ssDict.sDropdownType.LIST;

            if(option.search){
                var head = this._dropdown.querySelector("." + NodeType.DROP_DOWN_HEAD);
                if(!head){
                    head = createElement("div");
                    head.classList.add(NodeType.DROP_DOWN_HEAD);
                    this._dropdown.prepend(head);
                }

                if(isList){
                    var search = this._dropdown.querySelector("." + NodeType.DROP_DOWN_SEARCH);
                    if(!search){
                        search = createElement("input");
                        search.classList.add(NodeType.DROP_DOWN_SEARCH);
                        search.setAttribute("type", "text");
                        search.style.width = "100%";
                        head.appendChild(search);
    
                        search.addEventListener("mousedown", (e)=>{
                            e.stopPropagation();
                        });
        
                        search.addEventListener("keydown", (e)=>{
                            e.stopPropagation();
                            this.triggerKeydown(e);
                        });
        
                        search.addEventListener("input", (e)=>{
                            e.stopPropagation();
        
                            let lis = this._dropdown.querySelectorAll("ul.data-ul>li");
                            let display = null;
                            
                            for(let i = 0; i < lis.length; i++){
                                display = lis[i].getAttribute("data-display");
                                if(display && display.indexOf(search.value) > -1){
                                    lis[i].style.display = "";
                                }else{
                                    lis[i].style.display = "none";
                                }
                            }

                            if(this._currentItem){
                                this._currentItem.classList.remove("focus");
                                this._currentItem = null;
                            }
                        });
                    }

                    search.value = "";
                }
            }

            if(!isList){
                var head = this._dropdown.querySelector("." + NodeType.DROP_DOWN_HEAD);
                if(head){
                    this._dropdown.removeChild(head);
                }
            }

            var body = this._dropdown.querySelector("." + NodeType.DROP_DOWN_BODY);
            if(!body){
                body = createElement("div");
                body.classList.add(NodeType.DROP_DOWN_BODY);
                this._dropdown.appendChild(body);

                frame.appendChild(this._dropdown);
            }else{
                body.innerHTML = "";
            }
            
            if(option.data){
                if(isList){
                    var ul = createElement("ul");
                    ul.classList.add("data-ul");

                    if(option.multiSelect){
                        ul.classList.add("multi");
                    }

                    var item = null;

                    let blur = ()=>{
                        this.blur();
                    }

                    for(var i = 0; i < option.data.length; i++){
                        item = option.data[i];

                        let li = createElement("li");

                        li.setAttribute("data-display", item.display);
                        li.setAttribute("data-value", item.value);
                        li.option = option;
                        li.closeDropdown = blur;

                        if(option.multiSelect){
                            let label = createElement("label");
                            let input = createElement("input");
                            input.type = "checkbox";
                            label.innerText = item.display;
                            label.prepend(input);
                            li.appendChild(label);

                            input.addEventListener("change", this._change);
                        }else{
                            li.innerText = item.display;
                        }

                        ul.appendChild(li);
                    }
                    
                    body.appendChild(ul);
    
                    this._dropdown.removeEventListener("mousedown", this._mousedown);
                    this._dropdown.addEventListener("mousedown", this._mousedown);
                }else if(option.type == ssDict.sDropdownType.CALENDAR){
                    var calendar = components[option.type];
                    if(!calendar) return;

                    var view = calendar.createView({
                        type: option.viewMode ? option.viewMode : "datetime"
                    });
                    body.appendChild(view);

                    calendar.addEventListener("mousedown", (evt)=>{
                        if(evt.action == "refreshView"){
                            body.innerHTML = "";
                            body.appendChild(evt.view);
                        }else if(evt.value != null){
                            if(option.afterSelect){
                                option.afterSelect({
                                    display: evt.value,
                                    value: evt.value
                                }, this.__option);
                                this.blur();
                            }
                        }
                    });

                    this._dropdown.addEventListener("mousedown", (e)=>{
                        e.stopPropagation();
                    });
                }
            }else{
                
            }

            this.__option = {...option};
        },
        _change: function(e){
            e.stopPropagation();
            dropdown.selectItem(dropdown._getLi(e.target));
        },
        _mousedown: function(e){
            e.stopPropagation();
            let li = dropdown._getLi(e.target);
            if(li.option.multiSelect) return;
            dropdown.selectItem(li);
        },
        _getLi: function(target){
            if(target.tagName.toLowerCase() == "li"){
                return target;
            }else{
                return this._getLi(target.parentNode);
            }
        },
        selectItem(target){
            let item = {
                display: "",
                value: ""
            };

            if(target.option.multiSelect){
                let lis = this._dropdown.querySelectorAll("li");
                for(let i = 0; i < lis.length; i++){
                    if(lis[i].querySelector("input[type='checkbox']").checked){
                        item.display += lis[i].getAttribute("data-display") + ",";
                    }
                }
            }else{
                item.display = target.getAttribute("data-display");
                item.value = target.getAttribute("data-value");
                target.closeDropdown();
            }

            if(item.display.indexOf(",") > 0){
                item.display = item.display.substring(0, item.display.length - 1);
            }

            if(target.option.afterSelect){
                target.option.afterSelect(item, target.option)
            }
        },
        moveTo: function(rect, option){
            option = option ? option : {};

            this._build(option);

            if(!this._dropdown) return;

            this._dropdown.style.left = rect.left + "px";
            this._dropdown.style.top = rect.top + "px";

            if(util.isNumber(rect.width)){
                this._dropdown.style.width = rect.width + "px";
            }else{
                this._dropdown.style.width = rect.width;
            }

            if(util.isNumber(rect.height)){
                this._dropdown.style.height = rect.height + "px";
            }else{
                this._dropdown.style.height = rect.height;
            }
            
            this._dropdown.classList.add("active");

            if(option.search){
                setTimeout(()=>{
                    let search = this._dropdown.querySelector("." + NodeType.DROP_DOWN_SEARCH);
                    if(search){
                        search.focus();
                    }
                }, 100);
            }

            this.__boundRect = {
                left: rect.left,
                top: rect.top,
                width: this._dropdown.clientWidth,
                height: this._dropdown.clientHeight
            };
        },
        triggerKeydown(e){
            if(e.key == "Escape" || e.keyCode == 27){
                dropdown.blur();
            }else if(e.key == "ArrowDown" || e.keyCode == 40){
                this.listMove("down");
            }else if(e.key == "ArrowUp" || e.keyCode == 38){
                this.listMove("up");
            }else if(e.key == "Enter" || e.keyCode == 13){
                this.selectItem(this._currentItem);
            }
        },
        listMove(type){
            let lis = this._dropdown.querySelectorAll("li");
            if(!this._currentItem){
                this._currentItemIndex = 0;
                this._currentItem = lis[0];
                lis[0].classList.add("focus");
            }else{
                this._currentItem.classList.remove("focus");

                if(type == "down"){
                    this._currentItemIndex = this._currentItemIndex < lis.length - 1 
                        ? this._currentItemIndex + 1 
                        : lis.length - 1;
                }else if(type == "up"){
                    this._currentItemIndex = this._currentItemIndex <= 0 
                        ? 0 
                        : this._currentItemIndex - 1;
                }

                this._currentItem = lis[this._currentItemIndex];
                this._currentItem.classList.add("focus");
            }
        },
        blur: function(){
            if(!this._dropdown) return;
            this._dropdown.classList.remove("active");
        },
        getKey: function(){
            return this.__option && this.__option.key;
        },
        isInRange: function(offset){
            if(!this.__boundRect) return;

            var rect = this.__boundRect;
            return offset.left > rect.left && offset.left < rect.left + rect.width 
                && offset.top > rect.top && offset.top < rect.top + rect.height;
        },
        isActive: function(){
            return this._dropdown && this._dropdown.getAttribute("class").indexOf("active") > -1;
        },
        reset: function(){
            for(var i in this){
                if(i.indexOf("__") > -1){
                    delete this[i];
                }
            }
        },
        destroy: function(){
            this.blur();
            this._dropdown = null;
        }
    }

    /**
     * 创建DOM元素
     * @param {*} tagName 标签名
     * @returns 
     */
    var createElement = function(tagName){
        return document.createElement(tagName);
    }

    /**
     * 查询唯一DOM元素
     * @param {*} selector 
     * @returns 
     */
    var querySelector = function(selector){
        return container.querySelector(selector);
    }

    /**
     * 查询所有DOM元素
     * @param {*} selector 
     * @returns 
     */
    var querySelectorAll = function(selector){
        return container.querySelectorAll(selector);
    }

    /**
     * 将鼠标在页面上的坐标转换成元素上的坐标
     * @param {*} element 元素
     * @param {*} x 横坐标
     * @param {*} y 纵坐标
     * @param {*} ratio 设备像素比
     */
    var convertOffset = function(element, x, y, ratio){
        var offset = getElementOffset(element);
        var scrollOffset = getElementScrollOffset(element);

        return {
            left: (x + scrollOffset.left - offset.left) * ratio,
            top: (y + scrollOffset.top - offset.top) * ratio
        }
    }

    /**
     * 获取元素相对父元素的偏移量
     * @param {*} element 父元素
     * @param {*} position 获取第一个样式position对应的父元素
     * @param {*} clazz 获取第一个class包含clazz的元素
     * @returns 
     */
    var getElementOffset = function(element, position, clazz) {
        var left = element.offsetLeft;
        var top = element.offsetTop;

        var current = element.offsetParent;

        while (current) {
            if(current.style.position === position || 
                current.classList.contains(clazz)) break;

            left += current.offsetLeft;
            top += current.offsetTop;

            current = current.offsetParent;
        }

        return {
            left: left,
            top: top
        };
    }

    /**
     * 获取元素在整个页面的滚动距离
     * @param {*} element 
     * @returns 
     */
    var getElementScrollOffset = function(element) {
        var left = 0;
        var top = 0;
        var current = element.parentNode;

        while(current){
            top += typeof(current.scrollTop) == "number" ? current.scrollTop : 0;
            left += typeof(current.scrollLeft) == "number" ? current.scrollLeft : 0;

            //if(current.getAttribute("class").indexOf(NodeType.EDITOR) > -1) break;

            current = current.parentNode;
            if(!current || current.tagName.toLowerCase() == "html") break;
        }

        return {
            left: left,
            top: top
        }
    }

    /**
     * 缩放画布
     * @param {*} zScale 缩放比例
     */
    this.zoom = function(zScale){
        zScale = zScale > 3 ? 3 : zScale < 0.3 ? 0.3 : zScale;
        zScale = parseFloat(zScale.toFixed(1));
        ZOOM.scale = zScale;

        var pageList = querySelectorAll("." + NodeType.PAGE); 
        var canvasList = querySelectorAll("[" + NodeType.CANVAS_NAME + "]");

        var annotationWidth = pageSetting.annotationWidth;
        annotationWidth = annotationWidth == null ? 0 : annotationWidth;

        var width = pageSetting.size.width + annotationWidth;

        baseSize.width = width * zScale;
        baseSize.height = pageSetting.size.height * zScale;

        var ratio = getPixelRatio(canvasList[0].getContext("2d"));

        for(var i = 0; i < canvasList.length; i++){
            var canvas = canvasList[i];

            canvas.style.width = baseSize.width + "px";
            canvas.style.height = baseSize.height + "px";
    
            canvas.width = baseSize.width * ratio;
            canvas.height = baseSize.height * ratio;

            pageList[i].style.width = canvas.style.width;

            var ctx = canvas.getContext("2d");
            ctx.scale(zScale, zScale);
        }

        var page = pageList[0];

        var range = [];
        for(var j = 0; j < boundRectList.length; j++){
            var boundRect = boundRectList[j];

            boundRect.height = page.offsetHeight;
            boundRect.width = page.offsetWidth;

            page = frame.querySelector("[" + NodeType.PAGE_NAME + "='" + boundRect.pageIndex+"']");

            setFrameSizeAndPagePosition(frame, page, boundRect.pageIndex);

            boundRect.top = parseFloat(page.style.top);
            boundRect.left = parseFloat(page.style.left);

            range.push(boundRect.pageIndex);
        }

        processor.invalidate({range: range, zoomScale: ZOOM.scale});

        if(lastMouseEvtExt){
            lastMouseEvtExt.ratio = ratio / ZOOM.scale;
        }

        triggerListener("zoom", contextListeners, ZOOM.scale, 10);

        return zScale;
    }

    /**
     * 放大
     */
    this.zoomIn = function(){
        return this.zoom(ZOOM.scale + 0.1);
    }

    /**
     * 缩小
     */
    this.zoomOut = function(){
        return this.zoom(ZOOM.scale - 0.1);
    }

    /**
     * 设置当前句柄
     * @param {*} handleVal 设备句柄 
     * @returns 
     */
    var setHandle = function(handleVal){
        handle = handleVal;
    }

    /**
     * 根据句柄获取偏移量
     * @param {*} handle 设备句柄 
     * @param {*} autoCreate true：如果获取不到则自动创建
     * @returns 
     */
    var getOffset = function({handle, autoCreate}){
        var canvas = querySelector("[" + NodeType.CANVAS_NAME + "='" + handle + "']");
        if(!canvas && autoCreate){
            if(handle > processor.getPageSize()) return null;
            reCreatePage(reComputeRange(processor.getPageSize(), handle));
            canvas = querySelector("[" + NodeType.CANVAS_NAME + "='" + handle + "']");
        }
        return getElementOffset(canvas, "relative");
    }

    /**
     * 滚动到指定位置
     * @param {*} top 
     */
    var scrollTo = function(top){
        editor.scrollTop = top;
    }

    /**
     * 获取当前滚动距离
     * @returns 
     */
    var getScrollTop = function(){
        return editor.scrollTop;
    }

    /**
     * 重绘
     */
    this.invalidate = function(){
        var range = [];
        for(var j = 0; j < boundRectList.length; j++){
            range.push(boundRectList[j].pageIndex);
        }

        //TODO 要先清除再重绘
        processor.invalidate({range: range, zoomScale: ZOOM.scale});
    }

    /**
     * 获取有效的页码范围
     * @returns 
     */
    var getPageRange = function(){
        var range = [];
        for(var i in boundRectList){
            range.push(boundRectList[i].pageIndex);
        }
        return range;
    }

    /**
     * 创建分页
     * @param {*} opt 
     * @returns 
     */
    var createPage = function (opt) {
        var page = createElement("div");

        page.classList.add(NodeType.PAGE);
        page.setAttribute(NodeType.PAGE_NAME, opt.key);

        page.style.position = "absolute";
        page.style.width = opt.canvas.style.width;

        return page;
    }

    /**
     * 重新布局分页
     */
    var relayoutPage = function(opt){
        if(opt.multiplePageNum > 1 && opt.pageSize > 1){
            let leftNum = (opt.pageIndex - 1) % opt.multiplePageNum;
            let topNum = Math.floor((opt.pageIndex - 1) / opt.multiplePageNum);

            opt.page.style.left = opt.page.offsetWidth * leftNum + opt.multipleSplitWidth * leftNum + "px";
            opt.page.style.top = PAGE_OFFSET_TOP + (opt.page.offsetHeight * topNum) + "px";
        }else{
            opt.page.style.top = PAGE_OFFSET_TOP + (opt.page.offsetHeight * (opt.pageIndex - 1)) + "px";
        }
    }

    /**
     * 创建页面分隔节点
     * @param {*} opt 
     * @returns 
     */
    var createSpliter = function (opt) {
        var spliter = createElement("div");

        spliter.classList.add(NodeType.SPLITER);
        spliter.setAttribute(NodeType.SPLITER_NAME, opt.key);
        spliter.style.height = "15px";
        spliter.style.width = "100%";
        spliter.style.display = "inline-block";
        
        return spliter;
    }

    /**
     * 计算文本缩放比
     * @param {*} ctx
     * @returns 
     */
    var computeScale = function(ctx){
        var ratio = getPixelRatio(ctx);

        //此处将CSS的PX换成word使用的PT单位大小
        //pt：point，点，是印刷业一个标准的长度单位，1pt＝1/72英寸；
        var dpi = getDPI().x;

        //因为1px = 1/dpi英寸, 所以1pt和1px的倍数关系= dpi / 72
        scale = ratio * dpi / 72;

        return scale;
    }

    /**
     * 获取屏幕的设备像素比
     * @param {*} context 
     * @returns 
     */
    var getPixelRatio = function (context, time) {
        var backingStore = context.backingStorePixelRatio ||
                           context.webkitBackingStorePixelRatio ||
                           context.mozBackingStorePixelRatio ||
                           context.msBackingStorePixelRatio ||
                           context.oBackingStorePixelRatio ||
                           context.backingStorePixelRatio || 
                           1;

        ratio = (window.devicePixelRatio || 1) / backingStore;
        ratio = ratio * (time ? time : ratioTime);

        return parseFloat(ratio.toFixed(1));
    };

    /**
     * 获取DPI
     */
    var getDPI = function() {
        var dpi = {};
        if ( window.screen.deviceXDPI != undefined ) {
            dpi = {
                x: window.screen.deviceXDPI,
                y: window.screen.deviceYDPI
            }
        }
        else {
            var tmpNode = createElement("div");
            tmpNode.style.cssText = "width:1in;height:1in;position:absolute;left:0px;top:0px;z-index:99;visibility:hidden";
            document.body.appendChild( tmpNode );
            dpi = {
                x: parseInt( tmpNode.offsetWidth ),
                y: parseInt( tmpNode.offsetHeight )
            }
            tmpNode.parentNode.removeChild( tmpNode );
        }
        return dpi;
    }

    /**
     * 纸张规格，单位cm
     */
    var paperSpec = {
        A3: {
            value: "a3",
            unit: "cm",
            size: {
                width: 29.7,
                height: 42
            },
            padding: {
                top: 2.54,
                right: 3.18,
                bottom: 2.54,
                left: 3.18
            }
        },
        A4: {
            value: "a4",
            unit: "cm",
            size: {
                width: 21,
                height: 29.7
            },
            padding: {
                top: 2.54,
                right: 3.18,
                bottom: 2.54,
                left: 3.18
            }
        },
        A5: {
            value: "a5",
            unit: "cm",
            size: {
                width: 14.8,
                height: 21
            },
            padding: {
                top: 2.54,
                right: 3.18,
                bottom: 2.54,
                left: 3.18
            }
        },
        A6: {
            value: "a6",
            unit: "cm",
            size: {
                width: 10.5,
                height: 14.8
            },
            padding: {
                top: 2.54,
                right: 3.18,
                bottom: 2.54,
                left: 3.18
            }
        },
        A7: {
            value: "a7",
            unit: "cm",
            size: {
                width: 7.4,
                height: 10.5
            },
            padding: {
                top: 2.54,
                right: 3.18,
                bottom: 2.54,
                left: 3.18
            }
        }
    }

    /**
     * 获取当前设备下A4纸的像素
     * @param {*} ctx 绘制环境
     */
    var getPaperSize = function(ctx) {
        var spec = paperSpec["A4"];
        if(paper){
            if(paperSpec[paper.type]){
                spec = paperSpec[paper.type];
                if(validatePaper(paper, true)){
                    spec.padding = paper.padding;
                }
            }else if(validatePaper(paper)){
                spec = paper;
            }
        }

        var result = null;

        if(spec.unit && spec.unit.toLowerCase() == "cm"){
            result = convertToPageSize(spec, ctx);
        }else{
            //不指定单位一律按照像素处理
            result = {
                width: parseFloat(spec.size.width),
                height: parseFloat(spec.size.height),
                padding: {
                    top: parseFloat(spec.padding.top),
                    right: parseFloat(spec.padding.right),
                    bottom: parseFloat(spec.padding.bottom),
                    left: parseFloat(spec.padding.left)
                }
            }

            if(spec.headTop){
                result.headTop = parseFloat(spec.headTop);
            }

            if(spec.footBottom){
                result.footBottom = parseFloat(spec.footBottom);
            }
        }

        //当前页面属性
        if(!pageSetting){
            pageSetting = {
                size: {},
                padding: {}
            };
        }

        pageSetting.size.width = result.width;
        pageSetting.size.height = result.height;
        pageSetting.padding = {...result.padding};
        pageSetting.headTop = result.headTop;
        pageSetting.footBottom = result.footBottom;

        if(setting.annotation.display){
            //页面宽度是由canvas画布宽度决定的，这里的宽度是作为canvas样式宽度
            //画布宽度 = 样式宽度 × ratio
            result.width += setting.annotation.width;
            //内边距就是实际的内部边距，需要乘以设备像素比
            result.padding.right += setting.annotation.width * ratio;
            result.annotationWidth = setting.annotation.width;

            pageSetting.annotationWidth = setting.annotation.width;
        }

        return result;
    }

    /**
     * 校验paper参数是否有效
     * @param {*} paper 纸张设置
     * @param {*} padding 只校验padding的有效性
     * @returns 
     */
    var validatePaper = function(paper, padding){
        if(!paper) return false;

        var flg = true;

        if(padding){
            flg = paper.padding && paper.padding.top >= 0 && 
                paper.padding.right >= 0 && paper.padding.bottom >= 0 && paper.padding.left >= 0;
        }else{
            flg = paper.size && paper.padding;
            flg = flg && paper.size.width > 0 && paper.size.height > 0;

            flg = flg && paper.padding.top >= 0 && 
                paper.padding.right >= 0 && paper.padding.bottom >= 0 && paper.padding.left >= 0;
        }

        return flg;
    }

    /**
     * 厘米转像素
     * @param {*} cm 
     * @param {*} dpi 
     * @returns 
     */
    var cmToPx = (cm, dpi)=>{
        return cm / 2.54 * dpi;
    }

    /**
     * 四舍五入
     * @param {*} number 数字
     * @param {*} dpi 设备
     * @param {*} ratio 换算率
     * @param {*} decPlaces 保留小数位数
     * @returns 
     */
    var roundUpPx = (number, dpi, ratio, decPlaces)=>{
        decPlaces = decPlaces == null ? 1 : decPlaces;
        return parseFloat((cmToPx(number, dpi) * ratio).toFixed(decPlaces));
    };

    /**
     * 像素转厘米
     * @param {*} px 
     * @param {*} dpi 
     * @returns 
     */
    var pxToCm = (px, dpi)=>{
        return px / dpi * 2.54;
    };

    /**
     * 四舍五入
     * @param {*} number 数字
     * @param {*} dpi 设备
     * @param {*} ratio 换算率
     * @param {*} decPlaces 保留小数位数
     * @returns 
     */
    var roundUpCm = (number, dpi, ratio, decPlaces)=>{
        decPlaces = decPlaces == null ? 1 : decPlaces;
        return parseFloat((pxToCm(number, dpi) / ratio).toFixed(decPlaces));
    };

    /**
     * 将纸张大小转换成页面大小
     * @param {*} spec 纸张规格
     * @param {*} ctx 绘制环境
     * @returns 
     */
    var convertToPageSize = function(spec, ctx){
        var dpi = getDPI();
        var ratio = getPixelRatio(ctx);

        var size = {
            width: Math.ceil(cmToPx(spec.size.width, dpi.x)),
            height: Math.ceil(cmToPx(spec.size.height, dpi.y)),
            padding: {
                top: Math.ceil(cmToPx(spec.padding.top, dpi.x)) * ratio,
                right: Math.ceil(cmToPx(spec.padding.right, dpi.x)) * ratio,
                bottom: Math.ceil(cmToPx(spec.padding.bottom, dpi.x)) * ratio,
                left: Math.ceil(cmToPx(spec.padding.left, dpi.x)) * ratio
            }
        }

        if(spec.headTop){
            size.headTop = Math.ceil(cmToPx(spec.headTop, dpi.x)) * ratio;
        }

        if(spec.footBottom){
            size.footBottom = Math.ceil(cmToPx(spec.footBottom, dpi.x)) * ratio;
        }

        return size;
    }

    /**
     * 页面设置
     * @param {*} option 
     */
    this.pageSetting = function(option){
        if(option){
            mergePaper(option);

            option.zoomScale = ZOOM.scale;

            let json = formatData(processor.export());
            this.load(json, option);

            if(!option.notTriggerListener){
                triggerListener("pageSetting", contextListeners);
            }
        }
    }

    /**
     * 合并纸张参数
     * @param {*} option 
     */
    var mergePaper = function(option){
        if(!option || option.paper) return;

        let padding = null;
        if(option.padding){
            padding = option.padding;
            util.convertByMutiRatioDivideZoom(
                padding, 
                ratio, 
                ZOOM.scale
            );
        }

        let headFoot = null;
        if(option.headTop || option.footBottom){
            headFoot = {
                headTop: option.headTop,
                footBottom: option.footBottom
            };
            
            util.convertByMutiRatioDivideZoom(
                headFoot,
                ratio, 
                ZOOM.scale
            );
        }

        option.paper = cloneData(pageSetting);
        util.convertByMutiRatioZoom(option.paper, 1, 1);

        if(padding){
            for(let i in padding){
                if(padding[i] == null) continue;
                option.paper.padding[i] = padding[i];
            }
        }

        if(headFoot){
            for(let i in headFoot){
                if(headFoot[i] == null) continue;
                option.paper[i] = headFoot[i];
            }
        }
    }

    /**
     * 获取页面设置
     */
    this.getPageSetting = function(){
        var dpi = getDPI();
        var param = this.getRenderParam();

        var result = {
            size: {
                width: roundUpCm(pageSetting.size.width, dpi.x, 1),
                height: roundUpCm(pageSetting.size.height, dpi.y, 1),
            },
            padding: {
                top: roundUpCm(pageSetting.padding.top, dpi.x, param.ratio),
                right: roundUpCm(pageSetting.padding.right, dpi.x, param.ratio),
                bottom: roundUpCm(pageSetting.padding.bottom, dpi.x, param.ratio),
                left: roundUpCm(pageSetting.padding.left, dpi.x, param.ratio)
            }
        };

        if(pageSetting.headTop){
            result.headTop = roundUpCm(pageSetting.headTop, dpi.x, param.ratio);
        }else{
            let top = processor.getPageRect("head").top;
            result.headTop = roundUpCm(top, dpi.x, param.ratio);
        }

        if(pageSetting.footBottom){
            result.footBottom = roundUpCm(pageSetting.footBottom, dpi.x, param.ratio);
        }else{
            let pageRect = processor.getPageRect();
            let footRect = processor.getPageRect("foot");
            let top = pageRect.height - footRect.top - footRect.height;

            result.footBottom = roundUpCm(top, dpi.x, param.ratio);
        }

        return result;
    }

    /**
     * 创建最新页码
     * @returns 最新页码
     */
    var createNewPageIndex = function () {
        return ++currentPageIndex;
    }

    /**
     * 获取canvas大小
     * @param {*} ctx
     */
    var getCanvasSize = function(ctx) {
        if(baseSize){
            return baseSize;
        }

        baseSize = getPaperSize(ctx);
        
        return baseSize;
    }

    /**
     * 注册分页包围盒
     * @param {*} page 分页对象
     * @param {*} pageIndex 页码
     */
    var addPageBoundRect = function(page, pageIndex){
        var rect = {
            pageIndex: pageIndex,
            left: converter.px2Num(page.style.left),
            top: converter.px2Num(page.style.top),
            width: page.offsetWidth,
            height: page.offsetHeight
        };

        boundRectList.push(rect);

        boundRectList.sort((rect1, rect2)=>{ 
            return rect1.pageIndex - rect2.pageIndex 
        });
    }

    /**
     * 删除分页包围盒
     * @param {*} pageIndex 按指定的页码删除
     */
    var removePageBoundRect = function(pageIndex){
        for(var i = 0; i < boundRectList.length; i++){
            if(pageIndex == boundRectList[i].pageIndex){
                boundRectList.splice(i, 1);
                break;
            }
        }
    }

    /**
     * 选择器
     */
    var selection = {
        /**
         * 选择的起始元素
         */
        anchorNode: null,
        /**
         * 选择开始的 anchorNode 中的偏移量
         */
        anchorOffset: null,
        /**
         * 选择的结束元素
         */
        focusNode: null,
        /**
         * 选择开始处 focusNode 的偏移量
         */
        focusOffset: null,
        /**
         * 如果未选择任何内容（空范围）或不存在，则为 true
         */
        isCollapsed: null,
        /**
         * 选择中的范围数
         */
        rangeCount: 1,
        /**
         * 获取范围对象
         */
        getRangeAt: function(){
            return range;
        },
    };

    /**
     * 范围对象
     */
    var range = {
        /**
         * 起始元素
         */
        startContainer: null,
        /**
         * 起始元素偏移量
         */
        startOffset: null,
        /**
         * 结束元素
         */
        endContainer: null,
        /**
         * 结束元素偏移量
         */
        endOffset: null,
        /**
         * 偏移量属性名称集合
         */
        get offsetProps(){
            return [
                "pageIndex",
                "rowIndex",
                "elementIndex",
                "contentIndex",
                "next",
                "tableRowIndex",
                "tableColIndex"
            ];
        },
        /**
         * 设置collapsed
         */
        set collapsed(val){
            if(val){
                this.startOffset = null;
                this.endOffset = null;
            }
        },
        /**
         * 布尔值，如果范围在同一点上开始和结束（所以范围内没有内容）则为 true
         */
        get collapsed(){
            if(!this.startOffset || !this.endOffset) return true;
            return this.generateKey(this.startOffset) == this.generateKey(this.endOffset);
        },
        /**
         * 将偏移量生成一个字符串key
         */
        generateKey: function(offset){
            var key = "";
            var props = this.offsetProps;

            for(var i in props){
                if(props[i] == "next" && offset.next){
                    key += this.generateKey(offset.next);
                }else if(offset[props[i]] != null){
                    key += offset[props[i]];
                }
            }
    
            return key;
        },
        /**
         * 在范围内的所有节点中最近的共同祖先节点
         */
        commonAncestorContainer: null,
        /**
         * 用于将Range对象的起点指定为某个节点的起点，
         * 将Range对象的终点指定为该节点的终点。
         * Range对象所代表的区域包含该节点。
         * @param {*} node 
         */
        selectNode: function(node){
        },
        /**
         * 用于将Range对象的起点指定为某个节点中的所有内容的起点，
         * 将Range对象的终点指定为该节点所有内容的终点，
         * 也就是说使Range对象所代表的区域包含该节点的所有内容，
         * 但是不包括该节点标签本身。
         * @param {*} node 
         */
        selectNodeContents: function(node){

        },	
        /**
         * 用于将Range对象中所包含的内容从页面中删除。
         */
        deleteContents: function(){

        },	
        /**
         * 用于对当前的Range对象进行复制，该方法返回复制的Range对象。
         */
        cloneRange: function(){

        },	
        /**
         * 该方法返回的是一个复制的 DocumentFragment 对象，
         * 该对象为一个容器元素，当需要追加，修改，删除或查找页面上的元素时，
         * 该方法非常有用。
         */
        cloneContents: function(){

        },
        /**
         * 用于将Range对象所代表区域的HTML代码克隆到一个 DocumentFragment 中，
         * 然后从页面中删除这段HTML代码。
         */
        extraContents: function(){

        },	
        /**
         * 用于将指定的节点插入到某个Range对象所代表的区域中，
         * 插入位置为Range对象所代表区域的起点位置，如果该节点已经存在于页面之中，
         * 那么该节点将被移动到Range对象所代表区域的起点处。
         * @param {*} node 
         */
        insertNode: function(node){

        },	
        /**
         * 收缩选区
         * @param {*} toStart 是一个Boolean类型，
         * 1、false，表示将Range对象所代表的区域的起点移动到终点处；
         * 2、true，表示将Range对象所代表的区域的终点移动到该区域的起点处。
         */
        collapse: function(toStart){
        },	
        /**
         * 用于从浏览器中释放Range对象，释放之后将不能再访问该Range对象，否则将抛出脚本错误。
         */
        detach: function(){

        },	
        /**
         * 用于将某个节点中的某个位置指定为Range对象所代表区域的起点位置。
         * @param {*} node 
         * @param {*} num 是一个整型数值，有两种含义取决于node节点，
         * 1、当第一个参数node所代表的节点是一个内容为一段文字的时候，
         * 那么该参数值用于指定将第几个文字结束位置作为Range对象所代表区域的起点位置(num是从0开始)；
         * 2、当第一个参数node所代表的节点包括其他子节点时，
         * 该参数用于指定将第几个子节点的结束位置作为Range对象所代表区域的起点位置。 
         */
        setStart: function(node, num){

        },	
        /**
         * 用于将某个节点中的某处位置指定为Range对象所代表区域的结束位置。
         * @param {*} node 
         * @param {*} num 是一个整型数值，有两种含义取决于node节点，
         * 1、当第一个参数node所代表的节点是一个内容为一段文字的时候，
         * 那么该参数值用于指定将第几个文字结束位置作为Range对象所代表区域的起点位置(num是从0开始)；
         * 2、当第一个参数node所代表的节点包括其他子节点时，
         * 该参数用于指定将第几个子节点的结束位置作为Range对象所代表区域的起点位置。 
         */
        setEnd: function(node, num){

        },	
        /**
         * 用于将某个节点的起始位置指定为Range对象所代表区域的起点位置。
         * @param {*} node 
         */
        setStartBefore: function(node){

        },
        /**
         * 用于将某个节点的终点位置指定为Range对象所代表区域的起点位置。
         * @param {*} node 
         */
        setStartAfter: function(node){

        },
        /**
         * 用于将某个节点的起始位置指定为Range对象所代表区域的终点位置。
         * @param {*} node 
         */
        setEndBefore: function(node){

        },
        /**
         * 用于将某个节点的终点位置指定为Range对象所代表区域的终点位置。
         * @param {*} node 
         */
        setEndAfter: function(node){

        },
        /**
         * 重置范围
         */
        reset: function(){
            this.startContainer = null;
            this.startOffset = null;
            this.endContainer = null;
            this.endOffset = null;
            this.collapsed = true;

            for(var i in this){
                if(i.indexOf("__") == 0){
                    delete this[i];
                }
            }
        }
    };

    /**
     * 获取选择器
     * @returns 
     */
    var getSelection = function(){
        return selection;
    }

    /**
     * 设置当前被选择元素的样式
     * @param {*} style 样式
     * @param {*} join 和已存在的样式连接
     */
    this.setStyle = function(style, join){
        var range = selection.getRangeAt();
        processor.setStyle(style, range, join);

        textInput.focus(true);
    }

    /**
     * 段落属性
     */
    var paragraphProps = ["textIndent", "leftWholeIndent", "rightWholeIndent"];

    /**
     * 设置当前被选择的段落样式
     * @param {*} style 
     */
    this.setParagraphStyle = function(style){
        util.convertByMutiRatioDivideZoom(style, ratio, ZOOM.scale, paragraphProps);

        var range = selection.getRangeAt();
        processor.setParagraphStyle(style, range);

        // textInput.focus(true);
    }

    /**
     * 设置标题
     * @param {*} option 
     */
    this.setTitle = function(option){
        let pageIndex = processor.setTitle(option);
        triggerListener("titleChange", contextListeners, {pageIndex: pageIndex}, 10);
    }

    /**
     * 获取标题
     * @param {*} option
     */
    this.getTitles = function(option){
        return processor.getTitles(option);
    }

    /**
     * 插入回车换行
     * @param {*} opt 可选项
     */
    this.insertEnter = function(opt){
        var sEvt = createKeyEvent({});

        sEvt.enter = true;
        sEvt.keyName = "Enter";
        sEvt.eventType = "keydown";
        sEvt.outRect = calcOutRect();

        sDoc.getProcessor().dispatchEvent(sEvt);
    }

    /**
     * 插入文本
     * @param {*} text 
     * @param {*} opt
     */
    this.insertText = function(text, opt){
        sendText({}, text, opt);
    }

    /**
     * 在当前位置插入图片
     * @param {*} imageBase64 base64编码
     * @param {*} option 可选性
     */
    this.insertImage = function(imageBase64, option){
        if(option && option.openFile){
            util.openFile("image/*", (e)=>{
                if(e.target.files.length == 0) return;
                var file = e.target.files[0];
                var fr = new FileReader();
                fr.onloadend = (data)=>{
                    this.insertImage(fr.result);
                }
                fr.readAsDataURL(file);
            });
        }else{
            util.convertBase64ToImg(imageBase64, (img)=>{
                processor.acceptImage(img, {
                    width: img.width,
                    height: img.height,
                    ...option
                });
            });
    
            textInput.focus(true);
        }
    }

    /**
     * 插入表格
     * @param {*} option {rowCount: 1, colCount: 1}
     */
    this.insertTable = function(option){
        processor.insertTable(option);    
        textInput.focus(true);
    }

    /**
     * 对当前表格插入表格列
     * @param {*} option 
     */
    this.insertTableColumn = function(option){
        processor.insertTableColumn(option);
    }

    /**
     * 对当前表格插入表格行
     * @param {*} option 
     */
    this.insertTableRow = function(option){
        processor.insertTableRow(option);
    }

    /**
     * 对当前表格删除表格列
     * @param {*} option 
     */
    this.deleteTableColumn = function(option){
        processor.deleteTableColumn(option);
    }

    /**
     * 对当前表格删除表格行
     * @param {*} option 
     */
    this.deleteTableRow = function(option){
        processor.deleteTableRow(option);
    }

    /**
     * 合并单元格
     */
    this.mergeCell = function(){
        processor.mergeCell();
        textInput.focus(true);
    }

    /**
     * 拆分单元格
     */
    this.splitCell = function(option){
        processor.splitCell(option);
    }

    /**
     * 设置单元格边框
     * @param {*} option 
     */
    this.setCellBorder = function(option){
        processor.setCellBorder(option);
    }

    /**
     * 设置单元格内边距
     * @param {*} option 
     */
    this.setCellPadding = function(option){
        if(!option || !option.padding) return;

        var padding = option.padding; 
        var dpi = getDPI();

        for(let i in padding){
            if(i == "left" || i == "right"){
                padding[i] = roundUpPx(parseFloat(padding[i]), dpi.x, ratio, 0);
            }else{
                padding[i] = roundUpPx(parseFloat(padding[i]), dpi.y, ratio, 0);
            }
        }
        
        processor.setCellPadding(option);
    }

    /**
     * 获取单元格内边距
     * @param {*} option 
     */
    this.getCellPadding = function(option){
        var padding = processor.getCellPadding(option);
        
        var dpi = getDPI();

        for(let i in padding){
            if(i == "left" || i == "right"){
                padding[i] = roundUpCm(parseFloat(padding[i]), dpi.x, ratio, 2);
            }else{
                padding[i] = roundUpCm(parseFloat(padding[i]), dpi.y, ratio, 2);
            }
         }
        return padding;
    }

    /**
     * 设置单元格网格线
     * @param {*} option 
     */
    this.setCellGridLine = function(option){
        processor.setCellGridLine(option);
    }

    /**
     * 设置表格列宽
     * @param {*} option 
     */
    this.setTableColumnWidth = function(option){
        if(!option || !option.width) return;
        var dpi = getDPI();
        option.width = roundUpPx(parseFloat(option.width), dpi.x, ratio, 0);
        processor.setTableColumnWidth(option);
    }

    /**
     * 设置表格行高
     * @param {*} option 
     */
    this.setTableRowHeight = function(option){
        if(!option || !option.height) return;
        var dpi = getDPI();
        option.height = roundUpPx(parseFloat(option.height), dpi.y, ratio, 0);
        processor.setTableRowHeight(option);
    }

    /**
     * 设置为表格头
     * @param {*} option 
     */
    this.setTableRowHead = function(option){
        processor.setTableRowHead(option);
    }

    /**
     * 设置编辑器大小
     * @param size {width:0, height:0}
     */
    this.setSize = function(size){
        if(!size) return;

        if(size.width){
            editor.style.width = size.width + "px";
        }

        if(size.height){
            editor.style.height = size.height + "px";
        }
    }

    /**
     * 设置光标
     */
    this.setCursor = function(cursor, type){
        if(type == "canvas" || type == "document"){
            var canvasList = frame.querySelectorAll("canvas");
            var canvas = null;
    
            for(var i = 0; i < canvasList.length; i++){
                canvas = canvasList[i];
                canvas.style.cursor = cursor;
            }

            if(type == "document"){
                document.body.style.cursor = cursor;
            }
        } 
    }

    /**
     * 查找内容
     * @param {*} exp 查找的内容或表达式
     * @param {*} option 可选项
     */
    this.find = function(exp, option){
        if(option && option.highlightFindZone){
            processor.invalidate({
                range: option.pageRange, 
                highlightFindZone: option.highlightFindZone
            });

            scrollByType(option);
        }else{
            return processor.find(exp, option);
        }
    }

    /**
     * 按配置将滚动条滚动到指定位置
     * @param {*} option 
     */
    var scrollByType = function(option){
        let isPrev = null;

        if(option.type){
            isPrev = option.type == "prev";

            if(isPrev){
                option.pageRange.sort((a, b)=>{return a > b ? 1 : a < b ? -1 : 0});
            }else{
                //倒序
                option.pageRange.sort((a, b)=>{return a > b ? -1 : a < b ? 1 : 0});
            }
        }

        let canvas = getCanvas(null, null, option.pageRange[0]);

        let baseOffset = getElementOffset(canvas, "relative");
        let outRect = calcOutRect();

        let height = option.lastBoundRect.top + option.lastBoundRect.height;
        height = height / ratio * ZOOM.scale;

        let top = baseOffset.top + height - outRect.height;

        if(option.type){
            if(isPrev && top < editor.scrollTop - outRect.height){
                scrollTo(top);
            }else if(!isPrev && editor.scrollTop <= top){
                scrollTo(top);
            }
        }else{
            scrollTo(top);
        }
    }

    /**
     * 替换内容
     * @param {*} findExp 查找表达式
     * @param {*} option 可选项
     */
    this.replace = function(findExp, option){
        let replaceExp = option.replaceExp;
        return processor.replace(findExp, replaceExp, option);
    }

    /**
     * 复制
     */
    this.copy = function(){
        var sEvt = createKeyEvent({});

        sEvt.copy = true;
        sEvt.eventType = "keydown";
        sEvt.outRect = calcOutRect();

        sDoc.getProcessor().dispatchEvent(sEvt);
    }

    /**
     * 粘贴
     */
    this.paste = function(){
        if(!copyData) {
            fetchDataFromClipboard();
            return;
        }

        var sEvt = createKeyEvent({});

        sEvt.paste = true;
        sEvt.eventType = "keydown";
        sEvt.outRect = calcOutRect();

        if(copyData.type == "html"){
            var htmlJson = jsonc.parseHTML(copyData.value, "body");
            var localJson = jsonc.convertToLocal(htmlJson);
            sEvt.data = parseData(localJson);
        }

        sDoc.getProcessor().dispatchEvent(sEvt);

        setTimeout(()=>{
            textInput._textInput.dispatchEvent("paste");
        }, 2000);
    }

    /**
     * 从剪贴板中获取数据
     */
    var fetchDataFromClipboard = function(){
        const getClipboardData = new Promise((resolve) => {
            if (navigator.clipboard && navigator.clipboard.readText) {
                navigator.clipboard.read().then(text => resolve(text));
            } else {
                console.error('该浏览器不支持剪贴板API');
                resolve();
            }
        });
        
        getClipboardData.then(data => {
            let index = data[0].types.findIndex(t=>t.indexOf(ContentType.IMAGE) > -1);
            if(index > -1){
                data[0].getType(data[0].types[index]).then((data)=>{
                    util.imgBlobToBase64(data).then((base64)=>{
                        sDoc.insertImage(base64);
                    });
                });
            }else if(data[0].types.indexOf(ContentType.HTML) > -1){
                data[0].getType(ContentType.HTML).then((data)=>{
                    util.blobToString(data).then((text)=>{
                        var htmlJson = jsonc.parseHTML(text, "body");
                        var localJson = jsonc.convertToLocal(htmlJson);
                        var data = parseData(localJson);

                        sendRichText({}, data);
                    });
                });
            }else if(data[0].types.indexOf(ContentType.PLAIN) > -1){
                data[0].getType(ContentType.PLAIN).then((data)=>{
                    util.blobToString(data).then((text)=>{
                        var localJson = jsonc.parseTxt(text);
                        var data = parseData(localJson);

                        sendRichText({}, data);
                    });
                });
            }
        }).catch(() => {
            console.error("无法获取剪贴板的文本数据");
        });
    }

    /**
     * 剪切
     */
    this.cut = function(){
        var sEvt = createKeyEvent({});

        sEvt.cut = true;
        sEvt.eventType = "keydown";
        sEvt.outRect = calcOutRect();

        sDoc.getProcessor().dispatchEvent(sEvt);
    }

    /**
     * 在当前位置插入可编辑层
     * @param {*} option 可选项 {id:"", name:"", style:{}}
     */
    this.insertEditableLayer = function(option){
        processor.insertEditableLayer(option);
    }

    /**
     * 在当前位置插入页码
     * @param {*} option 可选项 {id:"", name:"", style:{}}
     */
    this.insertPageNum = function(option){
        processor.insertPageNum(option);
    }

    /**
     * 插入复选框
     * @param {*} option 可选项 {id:"", name:"", style:{}}
     */
    this.insertCheckBox = function(option){
        processor.insertCheckBox(option);
    }

    /**
     * 插入单选框
     * @param {*} option 可选项 {id:"", name:"", style:{}}
     */
    this.insertRadio = function(option){
        processor.insertRadio(option);
    }

    /**
     * 插入输入域
     * @param {*} option 
     */
    this.insertInputArea = function(option){
        processor.insertInputArea(option);
    }

    /**
     * 设置数据源
     * @param {*} json 
     */
    this.setDataSource = function(json){
        processor.setDataSource(json);
    }

    /**
     * 插入横线
     * @param {*} option 
     */
    this.insertLine = function(option){
        processor.insertLine(option);
    }

    /**
     * 插入水印
     * @param {*} option 
     */
    this.insertWatermark = function(option){
        processor.insertWatermark(option);
    }

    /**
     * 插入底纹
     * @param {*} option 
     */
    this.insertTexture = function(option){
        processor.insertTexture(option);
    }

    /**
     * 获取水印设置
     */
    this.getWatermark = function(){
        return processor.getWatermark();
    }

    /**
     * 获取水印设置
     */
    this.getTexture = function(){
        return processor.getTexture();
    }

    /**
     * 插入分页符
     * @param {*} option 
     */
    this.insertPageBreak = function(option){
        processor.insertPageBreak(option);
    }

    /**
     * 插入批注
     * @param {*} option 
     */
    this.insertAnnotation = function(option){
        option = option ? option : {};
        if(option.content == null) return;

        option.content = parseData(option.content);
        processor.insertAnnotation(option);
    }

    /**
     * 更新元素属性
     * @param {*} option 
     */
    this.updateAttributes = function(option){
        if(!option || !option.attributes) return;

        let attr = option.attributes;
        if(attr.width || attr.height){
            let dpi = getDPI();

            if(attr.width){
                attr.width = roundUpPx(attr.width, dpi.x, ratio);
            }

            if(attr.height){
                attr.height = roundUpPx(attr.height, dpi.x, ratio);
            }
        }

        processor.updateAttributes(option);
    }

    /**
     * 插入html
     * @param {*} html html文本字符串
     * @param {*} opt 可选项
     */
    this.insertHTML = function(content, opt){
        var htmlJson = jsonc.parseHTML(content, "body");
        var localJson = jsonc.convertToLocal(htmlJson);
        data = parseData(localJson);

        sendRichText({}, data);
    }

    /**
     * 设置选择区域
     * @param {*} range
     * @param {*} opt
     */
    this.setRange = function(range, opt){
        processor.setRange(range, opt);
    }

    /**
     * 撤销
     */
    this.undo = function(){
        var sEvt = createKeyEvent({});
        
        sEvt.undo = true;
        sEvt.eventType = "keydown";
        sEvt.outRect = calcOutRect();

        sDoc.getProcessor().dispatchEvent(sEvt);
    }

    /**
     * 重做
     */
    this.redo = function(){
        var sEvt = createKeyEvent({});
        
        sEvt.redo = true;
        sEvt.eventType = "keydown";
        sEvt.outRect = calcOutRect();

        sDoc.getProcessor().dispatchEvent(sEvt);
    }

    /**
     * 获取文档内所有标题信息
     */
    this.getAllTitleInfo = function(){
    }

    /**
     * 获取分页信息
     */
    this.getPageList = function(){
        return sDoc.getProcessor().getPageList();
    }

    /**
     * 获取当前文档编辑区域的包围盒
     */
    this.getEditBoundRect = function(){
        var rect = boundRectList.find(item=>item.pageIndex == parseInt(handle));
        if(!rect) return null;
        
        var page = getPage(handle);
        var canvas = getCanvas(handle);
        var pageSize = getPageSize();

        return {
            left: frame.offsetLeft + page.offsetLeft + canvas.offsetLeft,
            top: rect.top - editor.scrollTop + canvas.offsetTop,
            width: pageSize.width,
            height: pageSize.height
        };
    }

    /**
     * 获取文档内页面缩略图
     * @param {*} option
     */
    this.getPagePreview = function(option){
        var pageInfo = [];

        if(!option.range){
            option.range = [];

            let pageSize = processor.getPageSize();
            for(var i = 1; i <= pageSize; i++){
                option.range.push(i);
            }
        }

        if(!option.canvas){
            option.canvas = createCanvas({key: "canvas-preview"});
        }

        processor.invalidate({
            canvas: option.canvas,
            range: option.range, 
            print: true,
            afterPaintPage: (opt)=>{
                let ctx = option.canvas.getContext("2d");
                let operation = ctx.globalCompositeOperation;

                ctx.globalCompositeOperation = "destination-over";
                ctx.fillStyle = "#fff";
                ctx.fillRect(0, 0, option.canvas.width, option.canvas.height);
                ctx.globalCompositeOperation = operation;

                pageInfo[opt.pageIndex] = option.canvas.toDataURL("image/jpeg", 0.01);
            }
        });
        
        return pageInfo;
    }

    /**
     * 定位到指定页面
     * @param {*} option 
     */
    this.locatePage = function(option){
        option = option ? option : {};
        if(!option.pageIndex) return;

        var pageSize = processor.getPageSize();
        if(pageSize == 0) return;

        var offset = getOffset({
            handle: option.pageIndex,
            autoCreate: true
        });

        editor.scrollTop = offset.top;
    }

    /**
     * 定位到指定行
     * @param {*} option 
     */
    this.locateRow = function(option){
        scrollByType(option);

        let handle = option.pageRange[0];
        let sEvt = createMouseEventExt(handle);

        processor.setCursorByRect({
            sEvt: sEvt,
            pageIndex: handle,
            boundRect: option.lastBoundRect
        });
    }

    /**
     * 内容改变事件回调
     * @param {*} e
     */
    var contentChange = function(e){
        triggerListener("contentChange", contextListeners, e, 10);
    }

    /**
     * 内容统计事件回调
     * @param {*} e 
     */
    var contentStatistics = function(e){
        triggerListener("contentStatistics", contextListeners, e, 0);
    }

    /**
     * 当前页改变事件回调
     * @param {*} e 
     */
    var pageChange = function(e){
        triggerListener("pageChange", contextListeners, e, 10);
    }

    /**
     * 监听器
     */
    var contextListeners = {};

    /**
     * 文档增加监听
     * @param {*} name 
     * @param {*} callback 
     * @param {*} option
     */
    this.addEventListener = function(name, callback, option){
        if(!contextListeners[name]){
            contextListeners[name] = [];
        }

        contextListeners[name].push(callback);
    }

    /**
     * 文档增加多个监听
     * @param {*} names 
     * @param {*} callback 
     */
    this.addEventListeners = function(names, callback){
        for(var i in names){
            this.addEventListener(names[i], callback);
        }
    }

    /**
     * 监听器任务
     */
    var listenerTask = {};

    /**
     * 触发监听器
     * @param {*} eventType
     * @param {*} listeners 
     * @param {*} sEvt
     * @param {*} delay
     * @param {*} filter
     */
    var triggerListener = function(eventType, listeners, sEvt, delay, filter){
        if(!listeners[eventType]) return;
        if(listenerTask[eventType]){
            clearTimeout(listenerTask[eventType]);
            listenerTask[eventType] = null;
        }

        let listener = listeners[eventType];

        listenerTask[eventType] = setTimeout(()=>{
            sEvt = filter ? filter(sEvt) : sEvt;
            for(var i in listener){
                listener[i](sEvt, eventType);
            }
        }, delay > 0 ? delay : 50);
    }

    /**
     * 转成监听器事件参数
     * @param {*} sEvt 
     */
    var toListenerEventParam = function(sEvt){
        if(!sEvt || !sEvt.target) return sEvt;

        let result = {
            //段落信息
            paragraph: sEvt.target.paragraph,
            //本地事件参数
            nativeEvt: sEvt.nativeEvt,
            //元素属性
            attributes: processor.fetchAttributes(sEvt.target.ref),
            //位置
            position: sEvt.target.position
        };

        if(sEvt.target.next){
            let next = processor.extractOffset(sEvt.target, true);
            let column = next.table.getColumns(next.tableColIndex);
            let cell = next.table.getRows(next.tableRowIndex).getCells(next.tableColIndex);

            result.cell = {
                attributes: {
                    width: column.style.width,
                    height: processor.getElementHeight(cell)
                }
            };
            result.containerType = "sTable";
        }

        result.element = sEvt.target.ref;

        //输入域
        if(sEvt.belong){
            result.belong = sEvt.belong;
        }

        //段落相关属性转换
        util.convertByDivideRatio(result.paragraph, ratio, paragraphProps);

        var dpi = getDPI();

        //宽高转厘米
        if(result.attributes){
            if(result.attributes.width){
                result.attributes.width = roundUpCm(result.attributes.width, dpi.x, ratio);
            }
            if(result.attributes.height){
                result.attributes.height = roundUpCm(result.attributes.height, dpi.y, ratio);
            }
        }

        if(result.cell){
            if(result.cell.attributes.width){
                result.cell.attributes.width = roundUpCm(result.cell.attributes.width, dpi.x, ratio);
            }
            if(result.cell.attributes.height){
                result.cell.attributes.height = roundUpCm(result.cell.attributes.height, dpi.y, ratio);
            }
        }

        return result;
    }

    /**
     * 对dom节点添加调整大小的监听方法
     * @param {*} node 
     * @param {*} callback 
     */
    this.addResizeObserver = function(node, callback){
        var resizeObserver = new ResizeObserver(callback);
        resizeObserver.observe(node);
    }

}
